Related
I want to ask if there is a way to stop the auto looping. My goal is to have pause as default state and when user hover over my loop (images) it starts looping.
I managed to reverse the hover effect(mousenter starting the loop, mouseleave pause the loop), which was no problem. But i cant figure how to pause loop when my page loads.
HTML:
<div data-looper="go" class="looper">
<div class="looper-inner">
<div class="item">
<img src="http://lorempixel.com/320/240/sports" alt="">
</div>
<div class="item">
<img src="http://lorempixel.com/320/240/animals" alt="">
</div>
<div class="item">
<img src="http://lorempixel.com/320/240/food" alt="">
</div>
</div>
</div>
looper.js
;(function($, window, document, undefined) {
"use strict";
// css transition support detection
var cssTransitionSupport = (function(){
// body element
var body = document.body || document.documentElement,
// transition events with names
transEndEvents = {
'transition' : 'transitionend',
'WebkitTransition': 'webkitTransitionEnd',
'MozTransition' : 'transitionend',
'MsTransition' : 'MSTransitionEnd',
'OTransition' : 'oTransitionEnd otransitionend'
}, name;
// check for each transition type
for (name in transEndEvents){
// if transition type is supported
if (body.style[name] !== undefined) {
// return transition end event name
return transEndEvents[name];
}
}
// css transitions are not supported
return false;
})();
// class definition
var Looper = function(element, options) {
this.$element = $(element);
this.options = options;
this.looping = false;
// self-reference
var self = this;
// setup keyboard accessibility
this.$element.attr('tabindex', 0)
// use keydown, keypress not reliable in IE
.keydown(function(e){
// handle key
switch (e.which) {
// left arrow
case 37:
// go to previous item
self.prev();
break;
// right arrow
case 39:
// go to next item
self.next();
break;
// give control back to browser for other keys
default: return;
}
// prevent browser default action
e.preventDefault();
})
// ARIA
.find('.item').attr('aria-hidden', true);
// setup pause on hover
this.options.pause === 'hover' && this.$element
.on('mouseenter', $.proxy(this.loop, this))
.on('mouseleave', $.proxy(this.pause, this));
// trigger init event
this.$element.trigger('init');
};
// class prototype definition
Looper.prototype = {
/**
* Start auto-loop of items
* #param e
* #return Looper
*/
loop: function(e) {
if (!e) this.paused = false;
// check for interval
if (this.interval) {
// remove interval
clearInterval(this.interval);
this.interval = null;
}
// setup new loop interval
if (this.options.interval && !this.paused)
this.interval = setInterval($.proxy(this.next, this), this.options.interval);
// return reference to self for chaining
return this;
},
/**
* Pause auto-loop of items
* #param e
* #return Looper
*/
pause: function(e) {
if (!e) this.paused = true;
if (this.$element.find('.next, .prev').length && cssTransitionSupport) {
this.$element.trigger(cssTransitionSupport);
this.loop();
}
// remove interval
clearInterval(this.interval);
this.interval = null;
// return reference to self for chaining
return this;
},
/**
* Show next item
* #return Looper
*/
next: function() {
// return if looping
if (this.looping) return this;
// go to next item
return this.go('next');
},
/**
* Show previous item
* #return Looper
*/
prev: function() {
// return if looping
if (this.looping) return this;
// go to previous item
return this.go('prev');
},
/**
* Show item at specified position
* #param pos
* #return Looper
*/
to: function(pos) {
// return if looping
if (this.looping) return this;
// zero-base the position
--pos;
var // all items
$items = this.$element.find('.item'),
// active item
$active = $items.filter('.active'),
// active position
activePos = $items.index($active);
// return if position is out of range
if (pos > ($items.length - 1) || pos < 0) return this;
// if position is already active
if (activePos == pos)
// restart loop
return this.pause().loop();
// show item at position
return this.go($($items[pos]));
},
/**
* Show item
* #param to
* #return Looper
*/
go: function(to) {
// return if looping
if (this.looping) return this;
// all items
var $items = this.$element.find('.item');
// if no items, do nothing
if (!$items.length) return this;
// active item
var $active = $items.filter('.active'),
// active position
activePos = $items.index($active),
// next item to show
$next = typeof to == 'string' ? $active[to]() : to,
// next position
nextPos = $items.index($next),
// is there an auto-loop?
isLooping = this.interval,
// direction of next item
direction = typeof to == 'string'
? to
: ((activePos == -1 && nextPos == -1) || nextPos > activePos
? 'next'
: 'prev'),
// fallback if next item not found
fallback = direction == 'next' ? 'first' : 'last',
// self-reference
that = this,
// finish
complete = function(active, next, direction) {
// if not looping, already complete
if (!this.looping) return;
// set looping status to false
this.looping = false;
// update item classes
active.removeClass('active go ' + direction)
// update ARIA state
.attr('aria-hidden', true);
next.removeClass('go ' + direction).addClass('active')
// update ARIA state
.removeAttr('aria-hidden');
// custom event
var e = $.Event('shown', {
// related target is new active item
relatedTarget: next[0],
// related index is the index of the new active item
relatedIndex: $items.index(next)
});
// trigger shown event
this.$element.trigger(e);
};
// ensure next element
$next = $next && $next.length ? $next : $items[fallback]();
// return if next element is already active
if ($next.hasClass('active')) return this;
// custom event
var e = $.Event('show', {
// related target is next item to show
relatedTarget: $next[0],
relatedIndex: $items.index($next[0])
});
// trigger show event
this.$element.trigger(e);
// return if the event was canceled
if (e.isDefaultPrevented()) return this;
// set looping status to true
this.looping = true;
// if auto-looping, pause loop
if (isLooping) this.pause();
// if using a slide or cross-fade
if (this.$element.hasClass('slide') || this.$element.hasClass('xfade')) {
// if css transition support
if (cssTransitionSupport) {
// add direction class to active and next item
$next.addClass(direction);
$active.addClass('go ' + direction);
// force re-flow on next item
$next[0].offsetWidth;
// add go class to next item
$next.addClass('go');
// finish after transition
this.$element.one(cssTransitionSupport, function() {
// weird CSS transition when element is initially hidden
// may cause this event to fire twice with invalid $active
// element on first run
if (!$active.length) return;
complete.call(that, $active, $next, direction);
});
// ensure finish
setTimeout(function() {
complete.call(that, $active, $next, direction);
}, this.options.speed);
// no css transition support, use jQuery animation fallback
} else {
// setup animations
var active = {},
activeStyle,
next = {},
nextStyle;
// save original inline styles
activeStyle = $active.attr('style');
nextStyle = $next.attr('style');
// cross-fade
if (this.$element.hasClass('xfade')) {
active['opacity'] = 0;
next['opacity'] = 1;
$next.css('opacity', 0); // IE8
}
// slide
if (this.$element.hasClass('slide')) {
if (this.$element.hasClass('up')) {
active['top'] = direction == 'next' ? '-100%' : '100%';
next['top'] = 0;
} else if (this.$element.hasClass('down')) {
active['top'] = direction == 'next' ? '100%' : '-100%';
next['top'] = 0;
} else if (this.$element.hasClass('right')) {
active['left'] = direction == 'next' ? '100%' : '-100%';
next['left'] = 0;
} else {
active['left'] = direction == 'next' ? '-100%' : '100%';
next['left'] = 0;
}
}
$next.addClass(direction);
// do animations
$active.animate(active, this.options.speed);
$next.animate(next, this.options.speed, function(){
// finish
complete.call(that, $active, $next, direction);
// reset inline styles
$active.attr('style', activeStyle || '');
$next.attr('style', nextStyle || '');
});
}
// no animation
} else {
// finish
complete.call(that, $active, $next, direction);
}
// if auto-looping
if ((isLooping || (!isLooping && this.options.interval))
// and, no argument
&& (!to
// or, argument not equal to pause option
|| (typeof to == 'string' && to !== this.options.pause)
// or, argument is valid element object and pause option not equal to "to"
|| (to.length && this.options.pause !== 'to')))
// start/resume loop
this.loop();
// return reference to self for chaining
return this;
}
};
// plugin definition
$.fn.looper = function(option) {
// looper arguments
var looperArgs = arguments;
// for each matched element
return this.each(function() {
var $this = $(this),
looper = $this.data('looperjs'),
options = $.extend({}, $.fn.looper.defaults, $.isPlainObject(option) ? option : {}),
action = typeof option == 'string' ? option : option.looper,
args = option.args || (looperArgs.length > 1 && Array.prototype.slice.call(looperArgs, 1));
// ensure looper plugin
if (!looper) $this.data('looperjs', (looper = new Looper(this, options)));
// go to position if number
if (typeof option == 'number') looper.to(option);
// execute method if string
else if (action) {
// with arguments
if (args) looper[action].apply(looper, args.length ? args : ('' + args).split(','));
// without arguments
else looper[action]();
}
// if there's an interval, start the auto-loop
else if (options.interval) looper.loop()
// otherwise, go to first item in the loop
else looper.go();
});
};
// default options
$.fn.looper.defaults = {
// auto-loop interval
interval: 2000,
// when to pause auto-loop
pause: 'hover',
// css transition speed (must match css definition)
speed: 500
};
// constructor
$.fn.looper.Constructor = Looper;
// data api
$(function() {
// delegate click event on elements with data-looper attribute
$('body').on('click.looper', '[data-looper]', function(e) {
var $this = $(this);
// ignore the go method
if ($this.data('looper') == 'go') return;
// get element target via data (or href) attribute
var href, $target = $($this.data('target')
|| (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')), //strip for ie7
// setup options
options = $.extend({}, $target.data(), $this.data());
// start looper plugin
$target.looper(options);
// prevent default event
e.preventDefault();
});
// auto-init plugin
$('[data-looper="go"]').each(function(){
var $this = $(this);
$this.looper($this.data());
});
});
})(jQuery, window, document);
I did not find anything useful in plugin documentation.
Link to looper.js website
I'm using carousel.js, but when I use the controls, it starts to get buggy.
What I've noticed is:
When the autoplay showed all images and wants to show the first one again, it shows the first one for 0.5 seconds and returns to the last image and stays there.
The same goes for using the next button, it starts to bug at the last image (same as autoplay).
The prev button works fine, when I use that it doesn't show any problems at all.
When I remove the buttons, it works fine, no errors at all (Autoplay works fine).
Now the error that shows in console is: Uncaught TypeError: Cannot read property 'slice' of undefined, but I don't think this is the core of the error.
Because what I've noticed in inspect element is that when the error occurs, none of the images in the slides have the class: 'active' anymore, so the code won't work anymore. But I'm not sure what the cause of this is.
This is the code from my controls:
<a class="left carousel-control" href="#carousel-{$id}" data-slide="prev" role="button">
<span class="icon-arrow_left_nav icon-arrow-left2""></span>
</a>
<a class="right carousel-control" href="#carousel-{$id}" data-slide="next" role="button">
<span class="icon-arrow_right_nav icon-arrow-right2"></span>
</a>
And this is the code from the carousel.js file (standard)
/* ========================================================================
* Bootstrap: carousel.js v3.2.0
* http://getbootstrap.com/javascript/#carousel
* ========================================================================
* Copyright 2011-2014 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
+function ($) {
'use strict';
// CAROUSEL CLASS DEFINITION
// =========================
var Carousel = function (element, options) {
this.$element = $(element).on('keydown.bs.carousel', $.proxy(this.keydown, this))
this.$indicators = this.$element.find('.carousel-indicators')
this.options = options
this.paused =
this.sliding =
this.interval =
this.$active =
this.$items = null
this.options.pause == 'hover' && this.$element
.on('mouseenter.bs.carousel', $.proxy(this.pause, this))
.on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
}
Carousel.VERSION = '3.2.0'
Carousel.DEFAULTS = {
interval: 5000,
pause: 'hover',
wrap: true
}
Carousel.prototype.keydown = function (e) {
switch (e.which) {
case 37:
this.prev();
break
case 39:
this.next();
break
default:
return
}
e.preventDefault()
}
Carousel.prototype.cycle = function (e) {
e || (this.paused = false)
this.interval && clearInterval(this.interval)
this.options.interval
&& !this.paused
&& (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
return this
}
Carousel.prototype.getItemIndex = function (item) {
this.$items = item.parent().children('div.item')
return this.$items.index(item || this.$active)
}
Carousel.prototype.to = function (pos) {
var that = this
var activeIndex = this.getItemIndex(this.$active = this.$element.find('div.item.active'))
if (pos > (this.$items.length - 1) || pos < 0) return
if (this.sliding) return this.$element.one('slid.bs.carousel', function () {
that.to(pos)
}) // yes, "slid"
if (activeIndex == pos) return this.pause().cycle()
return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))
}
Carousel.prototype.pause = function (e) {
e || (this.paused = true)
if (this.$element.find('.next, .prev').length && $.support.transition) {
this.$element.trigger($.support.transition.end)
this.cycle(true)
}
this.interval = clearInterval(this.interval)
return this
}
Carousel.prototype.next = function () {
if (this.sliding) return
return this.slide('next')
}
Carousel.prototype.prev = function () {
if (this.sliding) return
return this.slide('prev')
}
Carousel.prototype.slide = function (type, next) {
var $active = this.$element.find('div.item.active')
var $next = next || $active[type]()
var isCycling = this.interval
var direction = type == 'next' ? 'left' : 'right'
var fallback = type == 'next' ? 'first' : 'last'
var that = this
if (!$next.length) {
if (!this.options.wrap) return
$next = this.$element.find('div.item')[fallback]()
}
if ($next.hasClass('active')) return (this.sliding = false)
var relatedTarget = $next[0]
var slideEvent = $.Event('slide.bs.carousel', {
relatedTarget: relatedTarget,
direction: direction
})
this.$element.trigger(slideEvent)
if (slideEvent.isDefaultPrevented()) return
this.sliding = true
isCycling && this.pause()
if (this.$indicators.length) {
this.$indicators.find('.active').removeClass('active')
var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)])
$nextIndicator && $nextIndicator.addClass('active')
}
var slidEvent = $.Event('slid.bs.carousel', {relatedTarget: relatedTarget, direction: direction}) // yes, "slid"
if ($.support.transition && this.$element.hasClass('slide')) {
$next.addClass(type)
$next[0].offsetWidth // force reflow
$active.addClass(direction)
$next.addClass(direction)
$active
.one('bsTransitionEnd', function () {
$next.removeClass([type, direction].join(' ')).addClass('active')
$active.removeClass(['active', direction].join(' '))
that.sliding = false
setTimeout(function () {
that.$element.trigger(slidEvent)
}, 0)
})
.emulateTransitionEnd($active.css('transition-duration').slice(0, -1) * 1000)
} else {
$active.removeClass('active')
$next.addClass('active')
this.sliding = false
this.$element.trigger(slidEvent)
}
isCycling && this.cycle()
return this
}
// CAROUSEL PLUGIN DEFINITION
// ==========================
function Plugin(option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.carousel')
var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)
var action = typeof option == 'string' ? option : options.slide
if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))
if (typeof option == 'number') data.to(option)
else if (action) data[action]()
else if (options.interval) data.pause().cycle()
})
}
var old = $.fn.carousel
$.fn.carousel = Plugin
$.fn.carousel.Constructor = Carousel
// CAROUSEL NO CONFLICT
// ====================
$.fn.carousel.noConflict = function () {
$.fn.carousel = old
return this
}
// CAROUSEL DATA-API
// =================
$(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {
var href
var $this = $(this)
var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
if (!$target.hasClass('carousel')) return
var options = $.extend({}, $target.data(), $this.data())
var slideIndex = $this.attr('data-slide-to')
if (slideIndex) options.interval = false
Plugin.call($target, options)
if (slideIndex) {
$target.data('bs.carousel').to(slideIndex)
}
e.preventDefault()
})
$(window).on('load', function () {
$('[data-ride="carousel"]').each(function () {
var $carousel = $(this)
Plugin.call($carousel, $carousel.data())
})
})
}(jQuery);
My task is to have a alert from ipad when i doubletap on my content.And i used this code to check my doubletap
(function($){
// Determine if we on iPhone or iPad
var isiOS = false;
var agent = navigator.userAgent.toLowerCase();
if(agent.indexOf('iphone') >= 0 || agent.indexOf('ipad') >= 0){
isiOS = true;
}
$.fn.doubletap = function(onDoubleTapCallback, onTapCallback, delay){
var eventName, action;
delay = delay == null? 500 : delay;
eventName = isiOS == true? 'touchend' : 'click';
$(this).bind(eventName, function(event){
var now = new Date().getTime();
var lastTouch = $(this).data('lastTouch') || now + 1 /** the first time this will make delta a negative number */;
var delta = now - lastTouch;
clearTimeout(action);
if(delta<500 && delta>0){
if(onDoubleTapCallback != null && typeof onDoubleTapCallback == 'function'){
onDoubleTapCallback(event);
}
}else{
$(this).data('lastTouch', now);
action = setTimeout(function(evt){
if(onTapCallback != null && typeof onTapCallback == 'function'){
onTapCallback(evt);
}
clearTimeout(action); // clear the timeout
}, delay, [event]);
}
$(this).data('lastTouch', now);
});
};)(jQuery);
And i call this code from my page
$(".selector").doubletap(
/** doubletap-dblclick callback */
function(event){
alert('double-tap');
},
/** touch-click callback (touch) */
function(event){
alert('single-tap');
},
/** doubletap-dblclick delay (default is 500 ms) */
400);
it works in my desktop.but when i open it in ipad it didn't work properly.please suggest me some solutions
Finally i used this code.
var elm1 = document.getElementById('test1');
var elm2 = document.getElementById('test2');
var timeout;
var lastTap = 0;
elm1.addEventListener('touchend', function(event) {
var currentTime = new Date().getTime();
var tapLength = currentTime - lastTap;
clearTimeout(timeout);
if (tapLength < 500 && tapLength > 0) {
elm2.innerHTML = 'Double Tap';
event.preventDefault();
} else {
elm2.innerHTML = 'Single Tap';
timeout = setTimeout(function() {
elm2.innerHTML = 'Single Tap (timeout)';
clearTimeout(timeout);
}, 500);
}
lastTap = currentTime;
});
Currently, the carousel does nothing unless a user interacts with it via the left or right arrows underneath the image container. I'm trying to add a function that will cause the slides to automatically flick through UNTIL a user interacts with these arrows, upon which, this function will stop.
Here is my current code that allows the user to scroll through the slides:
//Next Arrow Functionality
$('.arrow-next').click(function (e) {
var currentSlide = $('.active-slide');
var nextSlide = currentSlide.next();
var currentDot = $('.active-dot');
var nextDot = currentDot.next();
if (nextSlide.length == 0) {
nextSlide = $('.slide').first();
nextDot = $('.dot').first();
}
currentSlide.fadeOut(1000).removeClass('active-slide');
nextSlide.fadeIn(1000).addClass('active-slide');
currentDot.removeClass('active-dot');
nextDot.addClass('active-dot');
e.preventDefault();
});
//Previous Arrow Click Functionality
$('.arrow-prev').click(function (e) {
var currentSlide = $('.active-slide');
var prevSlide = currentSlide.prev();
var currentDot = $('.active-dot');
var prevDot = currentDot.prev();
if (prevSlide.length == 0) {
prevSlide = $('.slide').last();
prevDot = $('.dot').last();
}
currentSlide.fadeOut(1000).removeClass('active-slide');
prevSlide.fadeIn(1000).addClass('active-slide');
currentDot.removeClass('active-dot');
prevDot.addClass('active-dot');
e.preventDefault();
});
Thanks in advance!
Start by extracting out the functions to change slide.
function nextSlide () {
var currentSlide = $('.active-slide');
var nextSlide = currentSlide.next();
var currentDot = $('.active-dot');
var nextDot = currentDot.next();
if (nextSlide.length == 0) {
nextSlide = $('.slide').first();
nextDot = $('.dot').first();
}
currentSlide.fadeOut(1000).removeClass('active-slide');
nextSlide.fadeIn(1000).addClass('active-slide');
currentDot.removeClass('active-dot');
nextDot.addClass('active-dot');
}
function prevSlide () {
var currentSlide = $('.active-slide');
var prevSlide = currentSlide.prev();
var currentDot = $('.active-dot');
var prevDot = currentDot.prev();
if (prevSlide.length == 0) {
prevSlide = $('.slide').last();
prevDot = $('.dot').last();
}
currentSlide.fadeOut(1000).removeClass('active-slide');
prevSlide.fadeIn(1000).addClass('active-slide');
currentDot.removeClass('active-dot');
prevDot.addClass('active-dot');
}
Also create a new variable to keep track of if the user has interacted yet.
var userHasInteracted = false;
Then refactor the click handlers to use the newly created functions, and set the variable.
$('.arrow-next').click(function (e) {
e.preventDefault();
userHasInteracted = true;
nextSlide();
});
$('.arrow-prev').click(function (e) {
e.preventDefault();
userHasInteracted = true;
prevSlide();
});
Then add the logic to automatically advance the slide.
function autoAdvance () {
// Stop the loop if the user has interacted
if (userHasInteracted) return ;
// Jump to the next slide
nextSlide();
// Run me again in 8 seconds
setTimeout(autoAdvance, 8000);
}
// Advance the slide in 8 seconds
setTimeout(autoAdvance, 8000);
Something in this Curtains.js plug-in is preventing user text selection on my page. When I comment it out, I'm able to select text, when I put it back in, I'm not. Can someone identify it and tell me how to fix it? I'm at my wit's end.
<script>
/*
* Curtain.js - Create an unique page transitioning system
* ---
* Version: 2
* Copyright 2011, Victor Coulon (http://victorcoulon.fr)
* Released under the MIT Licence
*/
(function ( $, window, document, undefined ) {
var pluginName = 'curtain',
defaults = {
scrollSpeed: 400,
bodyHeight: 0,
linksArray: [],
mobile: false,
scrollButtons: {},
controls: null,
curtainLinks: '.curtain-links',
enableKeys: true,
easing: 'swing',
disabled: false,
nextSlide: function() {},
prevSlide: function() {}
};
// The actual plugin constructor
function Plugin( element, options ) {
var self = this;
// Public attributes
this.element = element;
this.options = $.extend( {}, defaults, options) ;
this._defaults = defaults;
this._name = pluginName;
this._ignoreHashChange = false;
this.init();
}
Plugin.prototype = {
init: function () {
var self = this;
// Cache element
this.$element = $(this.element);
this.$li = $(this.element).find('>li');
this.$liLength = this.$li.length;
self.$windowHeight = $(window).height();
self.$elDatas = {};
self.$document = $(document);
self.$window = $(window);
self.webkit = (navigator.userAgent.indexOf('Chrome') > -1 || navigator.userAgent.indexOf("Safari") > -1);
$.Android = (navigator.userAgent.match(/Android/i));
$.iPhone = ((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)));
$.iPad = ((navigator.userAgent.match(/iPad/i)));
$.iOs4 = (/OS [1-4]_[0-9_]+ like Mac OS X/i.test(navigator.userAgent));
if($.iPhone || $.iPad || $.Android || self.options.disabled){
this.options.mobile = true;
this.$li.css({position:'relative'});
this.$element.find('.fixed').css({position:'absolute'});
}
if(this.options.mobile){
this.scrollEl = this.$element;
} else if($.browser.mozilla || $.browser.msie) {
this.scrollEl = $('html');
} else {
this.scrollEl = $('body');
}
if(self.options.controls){
self.options.scrollButtons['up'] = $(self.options.controls).find('[href="#up"]');
self.options.scrollButtons['down'] = $(self.options.controls).find('[href="#down"]');
if(!$.iOs4 && ($.iPhone || $.iPad)){
self.$element.css({
position:'fixed',
top:0,
left:0,
right:0,
bottom:0,
'-webkit-overflow-scrolling':'touch',
overflow:'auto'
});
$(self.options.controls).css({position:'absolute'});
}
}
// When all image is loaded
var callbackImageLoaded = function(){
self.setDimensions();
self.$li.eq(0).addClass('current');
self.setCache();
if(!self.options.mobile){
if(self.$li.eq(1).length)
self.$li.eq(1).nextAll().addClass('hidden');
}
self.setEvents();
self.setLinks();
self.isHashIsOnList(location.hash.substring(1));
};
if(self.$element.find('img').length)
self.imageLoaded(callbackImageLoaded);
else
callbackImageLoaded();
},
// Events
scrollToPosition: function (direction){
var position = null,
self = this;
if(self.scrollEl.is(':animated')){
return false;
}
if(direction === 'up' || direction == 'down'){
// Keyboard event
var $next = (direction === 'up') ? self.$current.prev() : self.$current.next();
// Step in the current panel ?
if(self.$step){
if(!self.$current.find('.current-step').length){
self.$step.eq(0).addClass('current-step');
}
var $nextStep = (direction === 'up') ? self.$current.find('.current-step').prev('.step') : self.$current.find('.current-step').next('.step');
if($nextStep.length) {
position = (self.options.mobile) ? $nextStep.position().top + self.$elDatas[self.$current.index()]['data-position'] : $nextStep.position().top + self.$elDatas[self.$current.index()]['data-position'];
}
}
position = position || ((self.$elDatas[$next.index()] === undefined) ? null : self.$elDatas[$next.index()]['data-position']);
if(position !== null){
self.scrollEl.animate({
scrollTop: position
}, self.options.scrollSpeed, self.options.easing);
}
} else if(direction === 'top'){
self.scrollEl.animate({
scrollTop:0
}, self.options.scrollSpeed, self.options.easing);
} else if(direction === 'bottom'){
self.scrollEl.animate({
scrollTop:self.options.bodyHeight
}, self.options.scrollSpeed, self.options.easing);
} else {
var index = $("#"+direction).index(),
speed = Math.abs(self.currentIndex-index) * (this.options.scrollSpeed*4) / self.$liLength;
self.scrollEl.animate({
scrollTop:self.$elDatas[index]['data-position'] || null
}, (speed <= self.options.scrollSpeed) ? self.options.scrollSpeed : speed, this.options.easing);
}
},
scrollEvent: function() {
var self = this,
docTop = self.$document.scrollTop();
if(docTop < self.currentP && self.currentIndex > 0){
// Scroll to top
self._ignoreHashChange = true;
if(self.$current.prev().attr('id'))
self.setHash(self.$current.prev().attr('id'));
self.$current
.removeClass('current')
.css( (self.webkit) ? {'-webkit-transform': 'translateY(0px) translateZ(0)'} : {marginTop: 0} )
.nextAll().addClass('hidden').end()
.prev().addClass('current').removeClass('hidden');
self.setCache();
self.options.prevSlide();
} else if(docTop < (self.currentP + self.currentHeight)){
// Animate the current pannel during the scroll
if(self.webkit)
self.$current.css({'-webkit-transform': 'translateY('+(-(docTop-self.currentP))+'px) translateZ(0)' });
else
self.$current.css({marginTop: -(docTop-self.currentP) });
// If there is a fixed element in the current panel
if(self.$fixedLength){
var dataTop = parseInt(self.$fixed.attr('data-top'), 10);
if(docTop + self.$windowHeight >= self.currentP + self.currentHeight){
self.$fixed.css({
position: 'fixed'
});
} else {
self.$fixed.css({
position: 'absolute',
marginTop: Math.abs(docTop-self.currentP)
});
}
}
// If there is a step element in the current panel
if(self.$stepLength){
$.each(self.$step, function(i,el){
if(($(el).position().top+self.currentP) <= docTop+5 && $(el).position().top + self.currentP + $(el).height() >= docTop+5){
if(!$(el).hasClass('current-step')){
self.$step.removeClass('current-step');
$(el).addClass('current-step');
return false;
}
}
});
}
if(self.parallaxBg){
self.$current.css({
'background-position-y': docTop * self.parallaxBg
});
}
if(self.$fade.length){
self.$fade.css({
'opacity': 1-(docTop/ self.$fade.attr('data-fade'))
});
}
if(self.$slowScroll.length){
self.$slowScroll.css({
'margin-top' : (docTop / self.$slowScroll.attr('data-slow-scroll'))
});
}
} else {
// Scroll bottom
self._ignoreHashChange = true;
if(self.$current.next().attr('id'))
self.setHash(self.$current.next().attr('id'));
self.$current.removeClass('current')
.addClass('hidden')
.next('li').addClass('current').next('li').removeClass('hidden');
self.setCache();
self.options.nextSlide();
}
},
scrollMobileEvent: function() {
var self = this,
docTop = self.$element.scrollTop();
if(docTop+10 < self.currentP && self.currentIndex > 0){
// Scroll to top
self._ignoreHashChange = true;
if(self.$current.prev().attr('id'))
self.setHash(self.$current.prev().attr('id'));
self.$current.removeClass('current').prev().addClass('current');
self.setCache();
self.options.prevSlide();
} else if(docTop+10 < (self.currentP + self.currentHeight)){
// If there is a step element in the current panel
if(self.$stepLength){
$.each(self.$step, function(i,el){
if(($(el).position().top+self.currentP) <= docTop && (($(el).position().top+self.currentP) + $(el).outerHeight()) >= docTop){
if(!$(el).hasClass('current-step')){
self.$step.removeClass('current-step');
$(el).addClass('current-step');
}
}
});
}
} else {
// Scroll bottom
self._ignoreHashChange = true;
if(self.$current.next().attr('id'))
self.setHash(self.$current.next().attr('id'));
self.$current.removeClass('current').next().addClass('current');
self.setCache();
self.options.nextSlide();
}
},
// Setters
setDimensions: function(){
var self = this,
levelHeight = 0,
cover = false,
height = null;
self.$windowHeight = self.$window.height();
this.$li.each(function(index) {
var $self = $(this);
cover = $self.hasClass('cover');
if(cover){
$self.css({height: self.$windowHeight, zIndex: 999-index})
.attr('data-height',self.$windowHeight)
.attr('data-position',levelHeight);
self.$elDatas[$self.index()] = {
'data-height': parseInt(self.$windowHeight,10),
'data-position': parseInt(levelHeight, 10)
};
levelHeight += self.$windowHeight;
} else{
height = ($self.outerHeight() <= self.$windowHeight) ? self.$windowHeight : $self.outerHeight();
$self.css({minHeight: height, zIndex: 999-index})
.attr('data-height',height)
.attr('data-position',levelHeight);
self.$elDatas[$self.index()] = {
'data-height': parseInt(height, 10),
'data-position': parseInt(levelHeight, 10)
};
levelHeight += height;
}
if($self.find('.fixed').length){
var top = $self.find('.fixed').css('top');
$self.find('.fixed').attr('data-top', top);
}
});
if(!this.options.mobile)
this.setBodyHeight();
},
setEvents: function() {
var self = this;
$(window).on('resize', function(){
self.setDimensions();
});
if(self.options.mobile) {
self.$element.on('scroll', function(){
self.scrollMobileEvent();
});
} else {
self.$window.on('scroll', function(){
self.scrollEvent();
});
}
if(self.options.enableKeys) {
self.$document.on('keydown', function(e){
if(e.keyCode === 38 || e.keyCode === 37) {
self.scrollToPosition('up');
e.preventDefault();
return false;
}
if(e.keyCode === 40 || e.keyCode === 39){
self.scrollToPosition('down');
e.preventDefault();
return false;
}
// Home button
if(e.keyCode === 36){
self.scrollToPosition('top');
e.preventDefault();
return false;
}
// End button
if(e.keyCode === 35){
self.scrollToPosition('bottom');
e.preventDefault();
return false;
}
});
}
if(self.options.scrollButtons){
if(self.options.scrollButtons.up){
self.options.scrollButtons.up.on('click', function(e){
e.preventDefault();
self.scrollToPosition('up');
});
}
if(self.options.scrollButtons.down){
self.options.scrollButtons.down.on('click', function(e){
e.preventDefault();
self.scrollToPosition('down');
});
}
}
if(self.options.curtainLinks){
$(self.options.curtainLinks).on('click', function(e){
e.preventDefault();
var href = $(this).attr('href');
if(!self.isHashIsOnList(href.substring(1)) && position)
return false;
var position = self.$elDatas[$(href).index()]['data-position'] || null;
if(position){
self.scrollEl.animate({
scrollTop:position
}, self.options.scrollSpeed, self.options.easing);
}
return false;
});
}
self.$window.on("hashchange", function(event){
if(self._ignoreHashChange === false){
self.isHashIsOnList(location.hash.substring(1));
}
self._ignoreHashChange = false;
});
},
setBodyHeight: function(){
var h = 0;
for (var key in this.$elDatas) {
var obj = this.$elDatas[key];
h += obj['data-height'];
}
this.options.bodyHeight = h;
$('body').height(h);
},
setLinks: function(){
var self = this;
this.$li.each(function() {
var id = $(this).attr('id') || 0;
self.options.linksArray.push(id);
});
},
setHash: function(hash){
// "HARD FIX"
el = $('[href=#'+hash+']');
el.parent().siblings('li').removeClass('active');
el.parent().addClass('active');
if(history.pushState) {
history.pushState(null, null, '#'+hash);
}
else {
location.hash = hash;
}
},
setCache: function(){
var self = this;
self.$current = self.$element.find('.current');
self.$fixed = self.$current.find('.fixed');
self.$fixedLength = self.$fixed.length;
self.$step = self.$current.find('.step');
self.$stepLength = self.$step.length;
self.currentIndex = self.$current.index();
self.currentP = self.$elDatas[self.currentIndex]['data-position'];
self.currentHeight = self.$elDatas[self.currentIndex]['data-height'];
self.parallaxBg = self.$current.attr('data-parallax-background');
self.$fade = self.$current.find('[data-fade]');
self.$slowScroll = self.$current.find('[data-slow-scroll]');
},
// Utils
isHashIsOnList: function(hash){
var self = this;
$.each(self.options.linksArray, function(i,val){
if(val === hash){
self.scrollToPosition(hash);
return false;
}
});
},
readyElement: function(el,callback){
var interval = setInterval(function(){
if(el.length){
callback(el.length);
clearInterval(interval);
}
},60);
},
imageLoaded: function(callback){
var self = this,
elems = self.$element.find('img'),
len = elems.length,
blank = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
elems.bind('load.imgloaded',function(){
if (--len <= 0 && this.src !== blank || $(this).not(':visible')){
elems.unbind('load.imgloaded');
callback.call(elems,this);
}
}).each(function(){
if (this.complete || this.complete === undefined){
var src = this.src;
this.src = blank;
this.src = src;
}
});
}
};
$.fn[pluginName] = function ( options ) {
return this.each(function () {
if (!$.data(this, 'plugin_' + pluginName)) {
$.data(this, 'plugin_' + pluginName, new Plugin( this, options ));
}
});
};
})( jQuery, window, document );
</script>
First you would have to tell us how you are trying to select text (mouse, keyboard, touchscreen, etc.)
I bet my bitcoins on keyboard (but I don't have any).
Must be one of those
self.$document.on('keydown', function(e){
...
e.preventDefault();
which don't even document which keys these numbers stand for.
It's e.preventDefault() which prevents the default browser action from being performed.
If you're in Chrome devtools, you can use
monitorEvents(window, 'key')
to make sense of these.
Of course this bit may help a bit:
keyCode: 38
keyIdentifier: "Up"
So the code could be written readably by use of keyIdentifier instead of keyCode.
I don't know how compatible that would be across browsers.
Be warned that keydown keyCode values are different from keypress values (which actually insert real characters). keydown key codes will vary between keyboard layouts and locales.
See http://unixpapa.com/js/key.html for disgust and enlightenment, but mostly disgust.