diff --git a/css/app.css b/css/app.css index debeed7..28873f2 100644 --- a/css/app.css +++ b/css/app.css @@ -1,29 +1,167 @@ @charset "utf-8"; /* ------------------------------------------------------------- Launch Nasqueron - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Author: Dereckson + Author: Sébastien Santoro aka Dereckson, Tomas Sardyha Filename: app.css Version: 1.0 Licence: Creative Commons BY 3.0, BSD-2-Clause ------------------------------------------------------------- */ /* ------------------------------------------------------------- Table of contents - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - :: Main styles + :: Cards + :: Cards content + :: Horizontal scrollbar + :: Cards slider selector + :: Utilities classes */ /* ------------------------------------------------------------- Main styles - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ body { margin-top: 5vh; min-height: 150vh; background: #44484D url("../img/mountains.svg") repeat-x scroll bottom; } + +/* ------------------------------------------------------------- + Cards + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +.wrap { + position: relative; + margin: 3em 5vw; +} + +@media screen and (max-width: 1239px) { + section { + width: 78vw; + } +} + +@media screen and (min-width: 1240px) { + section { + width: 39vw; + } +} + +section { + float: left; + display: inline-block; + + min-height: 70vh; + padding: 1em !important; + margin: 0 5vw 0 0; + + border: solid 5px #2b3441; +} + +.section-odd { + background-color: #5587A2; + color: black; +} + +.section-even { + background-color: #67947D; + color: black; +} + +.section-third { + background-color: #F6D258; + color: black; +} + +/* ------------------------------------------------------------- + Cards content + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +section .paragraph { + margin-bottom: 2.5em; +} + +section p { + -webkit-margin-before: 0; +} + +.icon { + color: white; +} + +.section-third .icon { + color: #2B3441; +} + +/* ------------------------------------------------------------- + Horizontal scrollbar + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +.scrollbar { + margin: 0 0 1em 0; + height: 2px; + background: #ccc; + line-height: 0; +} + +.scrollbar .handle { + width: 100px; + height: 100%; + background: #292a33; + cursor: pointer; +} + +.scrollbar .handle .mousearea { + position: absolute; + top: -9px; + left: 0; + width: 100%; + height: 20px; +} + +/* ------------------------------------------------------------- + Cards slider selector + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +.pages { + list-style: none; + margin: 20px 0; + padding: 0; + text-align: center; +} + +.pages li { + display: inline-block; + width: 14px; + height: 14px; + margin: 0 4px; + text-indent: -999px; + border-radius: 10px; + cursor: pointer; + overflow: hidden; + background: #fff; + box-shadow: inset 0 0 0 1px rgba(0,0,0,.2); +} + +.pages li:hover { + background: #aaa; +} + +.pages li.active { + background: #666; +} + +/* ------------------------------------------------------------- + Utilities classes + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +.text-center { + text-align: center; +} diff --git a/humans.txt b/humans.txt index b1d9c94..564c878 100644 --- a/humans.txt +++ b/humans.txt @@ -1,33 +1,36 @@ # humanstxt.org/ # The humans responsible & technology colophon # TEAM Sébastien Santoro -- Lead developer -- @Dereckson # THANKS # TECHNOLOGY COLOPHON CSS3, HTML5 jQuery, Modernizr, Normalize.css Git, Phabricator +# JAVASCRIPT LIBRARIES -# MANIFESTO + Sly — Tomas Sardyha + +# NASQUERON MANIFESTO Nasqueron is a budding community of creative people, writers, developers and thinkers. You'll find here like-minded people to connect to, hang out, and build projects. We focus on free culture, ethics and to be a positive change. Our software is open source our datasources and content are licensed under CC-BY-SA or CC-BY license. We share values like respect, justice and equity. We like experiments, originality and to discover new thing. We are Nasqueron. diff --git a/img/logo-eglide-133px.png b/img/logo-eglide-133px.png new file mode 100644 index 0000000..fdec601 Binary files /dev/null and b/img/logo-eglide-133px.png differ diff --git a/img/logo-main-133px.png b/img/logo-main-133px.png new file mode 100644 index 0000000..9de7ad7 Binary files /dev/null and b/img/logo-main-133px.png differ diff --git a/index.html b/index.html index f61c653..426a317 100644 --- a/index.html +++ b/index.html @@ -1,31 +1,175 @@ - - + Launch Nasqueron + - -

Hello world! This is HTML5 Boilerplate.

+
+
+
+
+
+
+
+ +
+
+
+

Nasqueron logo

+ +

Our values

+ +
+
🏴
+

Nasqueron is a budding community of creative people, writers, developers and thinkers.

+
+ +
+
🈁
+

You'll find here like-minded people to connect to, hang out, and build projects.

+
+ +
+
📃
+

We focus on free culture, ethics and to be a positive change. Our software is open source our datasources and content are licensed under CC-BY-SA or CC-BY license. We share values like respect, justice and equity.

+
+ +
+
❤️
+

We like experiments, originality and to discover new thing.

+
+ +
+
👽
+

We are Nasqueron.

+
+
+ +
+

Nasqueron logo

+ +

Infrastructure

+ +
+
💎
+

In 2016, we've focused to build a great infrastructure. Servers, continuous integration, places to communicate, notifications, operations workflows.

+

We believe this infrastructure first provides a great added value to any project: everything is in place and polished.

+

The infrastructure is open and managed by the Operations special interest group. A configuration-as-code repository is used, so it's reliable, well documented and open source.

+
+ +
+
🔨
+

To communicate, we've deployed mailing lists, Etherpad, a forum, a wiki, IRC bots with a great glue notifications tooling.

+

To craft software, we've Phabricator and Jenkins instances. Did we tell you we've notifications sorted?

+

To host apps, we've a PaaS built on Docker. And an server dedicated to IRC. And a development and staging server.

+
+ +
+
🏺
+

So whatever project you wish to start, we're here to support you and provide you with an awesome infrastructure.

+
+
+ +
+

Nasqueron logo

+ +

Software we develop

+ +

Tasacora

+

A cartography solution to build a vectorial thematic map from a blank map and a dataset.

+

Lead developer: Rama — Lead geographer: Kumkum

+ +

Notifications center

+

A gateway to accept webhooks and payloads from GitHub, DockerHub, Jenkins, Phabricator and other CI products.

+

It then standardizes notifications into an unified format.
Emits them to an AMQP broker.

+

Intelligent bus with if this then that behavior.

+

Lead developer: Dereckson

+ +

Docker images for Phabricator

+

We maintain state of art Docker images to run Phabricator, Aphlict or Arcanist in Docker.

+

Team: SandlaythDereckson

+ +

DynBridge

+

TCL ⋄ Python bridge

+

Lead developer: Xavier Combelle

+
+ +
+

Eglide logo

+ +

Eglide

+ +

We're baking a great shell hosting service. Claim a little space for your files, an IRC connection for you or your bot, a website. Something where to code, experiment, connect with like-minded people.

+ +

Für die Freikultur. Eglide is a free culture project. Everyone is welcome to participate or use it.

+ +

Our involvement

+ +

The service is managed by Nasqueron.

+ +

Nasqueron is committed to provide ITC management at operations and devops level to free culture projects.

+
+ +
+

Nasqueron logo

+ +

Governance

+

Nasqueron is organised in special interest groups (SIG). Each group is autonomous and self managed. Decisions for the project as a whole are taken by consensus.

+ +

Some SIGs take responsibilities for the project as a whole, for example the Operations SIG build and maintain our infrastructure. Others can represent the project to third parties like the IRC SIG to the Freenode network.

+ +

We aren't incorporated: a de facto status is currently enough for our needs. If we decide to incorporate, it will be as a non-profit foundation.

+ + +

Covenant

+ +

We as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.

+
+ +
+

Nasqueron logo

+ +

More information?

+ +
📧
+

You can join our Launch mailing list. Send a mail to launch-subscribe (at) lists.nasqueron.org.

+ +
💬
+

You can also say hello on IRC. We meet in Freenode #nasqueron.

+ +
🈴
+

If you consider yourself a part of Nasqueron, follow this guide to join.

+ +
📛
+

Finally, if you come to the FOSDEM 2017 edition, you can meet some members.

+
+
+
+ + +
+
+ diff --git a/js/main.js b/js/main.js index e69de29..715caf8 100644 --- a/js/main.js +++ b/js/main.js @@ -0,0 +1,34 @@ +jQuery(function($){ + 'use strict'; + + // ------------------------------------------------------------- + // Basic Navigation + // ------------------------------------------------------------- + (function () { + var frame = $('#cards'); + var wrap = frame.parent(); + + // Call Sly on frame + frame.sly({ + horizontal: 1, + itemNav: 'basic', + smart: 1, + activateOn: 'click', + mouseDragging: 1, + touchDragging: 1, + releaseSwing: 1, + startAt: 0, + scrollBar: wrap.find('.scrollbar'), + scrollBy: 1, + pagesBar: wrap.find('.pages'), + activatePageOn: 'click', + speed: 300, + elasticBounds: 1, + easing: 'easeOutExpo', + dragHandle: 1, + dynamicHandle: 1, + clickBar: 1, + }); + }()); + +}); diff --git a/js/plugins.js b/js/plugins.js index f887480..4453920 100644 --- a/js/plugins.js +++ b/js/plugins.js @@ -1,24 +1,32 @@ // Avoid `console` errors in browsers that lack a console. (function() { var method; var noop = function () {}; var methods = [ 'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd', 'timeline', 'timelineEnd', 'timeStamp', 'trace', 'warn' ]; var length = methods.length; var console = (window.console = window.console || {}); while (length--) { method = methods[length]; // Only stub undefined methods. if (!console[method]) { console[method] = noop; } } }()); -// Place any jQuery/helper plugins in here. +// jQuery easing 1.3 +jQuery.easing.jswing=jQuery.easing.swing; +jQuery.extend(jQuery.easing,{def:"easeOutQuad",swing:function(e,a,c,b,d){return jQuery.easing[jQuery.easing.def](e,a,c,b,d)},easeInQuad:function(e,a,c,b,d){return b*(a/=d)*a+c},easeOutQuad:function(e,a,c,b,d){return-b*(a/=d)*(a-2)+c},easeInOutQuad:function(e,a,c,b,d){return 1>(a/=d/2)?b/2*a*a+c:-b/2*(--a*(a-2)-1)+c},easeInCubic:function(e,a,c,b,d){return b*(a/=d)*a*a+c},easeOutCubic:function(e,a,c,b,d){return b*((a=a/d-1)*a*a+1)+c},easeInOutCubic:function(e,a,c,b,d){return 1>(a/=d/2)?b/2*a*a*a+c: + b/2*((a-=2)*a*a+2)+c},easeInQuart:function(e,a,c,b,d){return b*(a/=d)*a*a*a+c},easeOutQuart:function(e,a,c,b,d){return-b*((a=a/d-1)*a*a*a-1)+c},easeInOutQuart:function(e,a,c,b,d){return 1>(a/=d/2)?b/2*a*a*a*a+c:-b/2*((a-=2)*a*a*a-2)+c},easeInQuint:function(e,a,c,b,d){return b*(a/=d)*a*a*a*a+c},easeOutQuint:function(e,a,c,b,d){return b*((a=a/d-1)*a*a*a*a+1)+c},easeInOutQuint:function(e,a,c,b,d){return 1>(a/=d/2)?b/2*a*a*a*a*a+c:b/2*((a-=2)*a*a*a*a+2)+c},easeInSine:function(e,a,c,b,d){return-b*Math.cos(a/ + d*(Math.PI/2))+b+c},easeOutSine:function(e,a,c,b,d){return b*Math.sin(a/d*(Math.PI/2))+c},easeInOutSine:function(e,a,c,b,d){return-b/2*(Math.cos(Math.PI*a/d)-1)+c},easeInExpo:function(e,a,c,b,d){return 0==a?c:b*Math.pow(2,10*(a/d-1))+c},easeOutExpo:function(e,a,c,b,d){return a==d?c+b:b*(-Math.pow(2,-10*a/d)+1)+c},easeInOutExpo:function(e,a,c,b,d){return 0==a?c:a==d?c+b:1>(a/=d/2)?b/2*Math.pow(2,10*(a-1))+c:b/2*(-Math.pow(2,-10*--a)+2)+c},easeInCirc:function(e,a,c,b,d){return-b*(Math.sqrt(1-(a/=d)* + a)-1)+c},easeOutCirc:function(e,a,c,b,d){return b*Math.sqrt(1-(a=a/d-1)*a)+c},easeInOutCirc:function(e,a,c,b,d){return 1>(a/=d/2)?-b/2*(Math.sqrt(1-a*a)-1)+c:b/2*(Math.sqrt(1-(a-=2)*a)+1)+c},easeInElastic:function(e,a,c,b,d){var e=1.70158,f=0,g=b;if(0==a)return c;if(1==(a/=d))return c+b;f||(f=0.3*d);ga?-0.5*g*Math.pow(2,10*(a-=1))*Math.sin((a*d-e)*2*Math.PI/f)+c:0.5*g*Math.pow(2,-10*(a-=1))*Math.sin((a*d-e)*2*Math.PI/f)+b+c},easeInBack:function(e,a,c,b,d,f){void 0== +f&&(f=1.70158);return b*(a/=d)*a*((f+1)*a-f)+c},easeOutBack:function(e,a,c,b,d,f){void 0==f&&(f=1.70158);return b*((a=a/d-1)*a*((f+1)*a+f)+1)+c},easeInOutBack:function(e,a,c,b,d,f){void 0==f&&(f=1.70158);return 1>(a/=d/2)?b/2*a*a*(((f*=1.525)+1)*a-f)+c:b/2*((a-=2)*a*(((f*=1.525)+1)*a+f)+2)+c},easeInBounce:function(e,a,c,b,d){return b-jQuery.easing.easeOutBounce(e,d-a,0,b,d)+c},easeOutBounce:function(e,a,c,b,d){return(a/=d)<1/2.75?b*7.5625*a*a+c:a<2/2.75?b*(7.5625*(a-=1.5/2.75)*a+0.75)+c:a<2.5/2.75? + b*(7.5625*(a-=2.25/2.75)*a+0.9375)+c:b*(7.5625*(a-=2.625/2.75)*a+0.984375)+c},easeInOutBounce:function(e,a,c,b,d){return a 0) { + slideeSize -= min(itemMarginStart, itemMarginEnd); + } + } + + // Things to be done on last item + if (i === lastItemIndex) { + item.end += paddingEnd; + slideeSize += paddingEnd; + ignoredMargin = singleSpaced ? itemMarginEnd : 0; + } + + // Add item object to items array + items.push(item); + lastItem = item; + }); + + // Resize SLIDEE to fit all items + $slidee[0].style[o.horizontal ? 'width' : 'height'] = (borderBox ? slideeSize: slideeSize - paddingStart - paddingEnd) + 'px'; + + // Adjust internal SLIDEE size for last margin + slideeSize -= ignoredMargin; + + // Set limits + if (items.length) { + pos.start = items[0][forceCenteredNav ? 'center' : 'start']; + pos.end = forceCenteredNav ? lastItem.center : frameSize < slideeSize ? lastItem.end : pos.start; + } else { + pos.start = pos.end = 0; + } + } + + // Calculate SLIDEE center position + pos.center = round(pos.end / 2 + pos.start / 2); + + // Update relative positions + updateRelatives(); + + // Scrollbar + if ($handle.length && sbSize > 0) { + // Stretch scrollbar handle to represent the visible area + if (o.dynamicHandle) { + handleSize = pos.start === pos.end ? sbSize : round(sbSize * frameSize / slideeSize); + handleSize = within(handleSize, o.minHandleSize, sbSize); + $handle[0].style[o.horizontal ? 'width' : 'height'] = handleSize + 'px'; + } else { + handleSize = $handle[o.horizontal ? 'outerWidth' : 'outerHeight'](); + } + + hPos.end = sbSize - handleSize; + + if (!renderID) { + syncScrollbar(); + } + } + + // Pages + if (!parallax && frameSize > 0) { + var tempPagePos = pos.start; + var pagesHtml = ''; + + // Populate pages array + if (itemNav) { + $.each(items, function (i, item) { + if (forceCenteredNav) { + pages.push(item.center); + } else if (item.start + item.size > tempPagePos && tempPagePos <= pos.end) { + tempPagePos = item.start; + pages.push(tempPagePos); + tempPagePos += frameSize; + if (tempPagePos > pos.end && tempPagePos < pos.end + frameSize) { + pages.push(pos.end); + } + } + }); + } else { + while (tempPagePos - frameSize < pos.end) { + pages.push(tempPagePos); + tempPagePos += frameSize; + } + } + + // Pages bar + if ($pb[0] && lastPagesCount !== pages.length) { + for (var i = 0; i < pages.length; i++) { + pagesHtml += o.pageBuilder.call(self, i); + } + $pages = $pb.html(pagesHtml).children(); + $pages.eq(rel.activePage).addClass(o.activeClass); + } + } + + // Extend relative variables object with some useful info + rel.slideeSize = slideeSize; + rel.frameSize = frameSize; + rel.sbSize = sbSize; + rel.handleSize = handleSize; + + // Activate requested position + if (itemNav) { + if (isInit && o.startAt != null) { + activate(o.startAt); + self[centeredNav ? 'toCenter' : 'toStart'](o.startAt); + } + // Fix possible overflowing + var activeItem = items[rel.activeItem]; + slideTo(centeredNav && activeItem ? activeItem.center : within(pos.dest, pos.start, pos.end)); + } else { + if (isInit) { + if (o.startAt != null) slideTo(o.startAt, 1); + } else { + // Fix possible overflowing + slideTo(within(pos.dest, pos.start, pos.end)); + } + } + + // Trigger load event + trigger('load'); + } + self.reload = function () { load(); }; + + /** + * Animate to a position. + * + * @param {Int} newPos New position. + * @param {Bool} immediate Reposition immediately without an animation. + * @param {Bool} dontAlign Do not align items, use the raw position passed in first argument. + * + * @return {Void} + */ + function slideTo(newPos, immediate, dontAlign) { + // Align items + if (itemNav && dragging.released && !dontAlign) { + var tempRel = getRelatives(newPos); + var isNotBordering = newPos > pos.start && newPos < pos.end; + + if (centeredNav) { + if (isNotBordering) { + newPos = items[tempRel.centerItem].center; + } + if (forceCenteredNav && o.activateMiddle) { + activate(tempRel.centerItem); + } + } else if (isNotBordering) { + newPos = items[tempRel.firstItem].start; + } + } + + // Handle overflowing position limits + if (dragging.init && dragging.slidee && o.elasticBounds) { + if (newPos > pos.end) { + newPos = pos.end + (newPos - pos.end) / 6; + } else if (newPos < pos.start) { + newPos = pos.start + (newPos - pos.start) / 6; + } + } else { + newPos = within(newPos, pos.start, pos.end); + } + + // Update the animation object + animation.start = +new Date(); + animation.time = 0; + animation.from = pos.cur; + animation.to = newPos; + animation.delta = newPos - pos.cur; + animation.tweesing = dragging.tweese || dragging.init && !dragging.slidee; + animation.immediate = !animation.tweesing && (immediate || dragging.init && dragging.slidee || !o.speed); + + // Reset dragging tweesing request + dragging.tweese = 0; + + // Start animation rendering + if (newPos !== pos.dest) { + pos.dest = newPos; + trigger('change'); + if (!renderID) { + render(); + } + } + + // Reset next cycle timeout + resetCycle(); + + // Synchronize states + updateRelatives(); + updateButtonsState(); + syncPagesbar(); + } + + /** + * Render animation frame. + * + * @return {Void} + */ + function render() { + if (!self.initialized) { + return; + } + + // If first render call, wait for next animationFrame + if (!renderID) { + renderID = rAF(render); + if (dragging.released) { + trigger('moveStart'); + } + return; + } + + // If immediate repositioning is requested, don't animate. + if (animation.immediate) { + pos.cur = animation.to; + } + // Use tweesing for animations without known end point + else if (animation.tweesing) { + animation.tweeseDelta = animation.to - pos.cur; + // Fuck Zeno's paradox + if (abs(animation.tweeseDelta) < 0.1) { + pos.cur = animation.to; + } else { + pos.cur += animation.tweeseDelta * (dragging.released ? o.swingSpeed : o.syncSpeed); + } + } + // Use tweening for basic animations with known end point + else { + animation.time = min(+new Date() - animation.start, o.speed); + pos.cur = animation.from + animation.delta * $.easing[o.easing](animation.time/o.speed, animation.time, 0, 1, o.speed); + } + + // If there is nothing more to render break the rendering loop, otherwise request new animation frame. + if (animation.to === pos.cur) { + pos.cur = animation.to; + dragging.tweese = renderID = 0; + } else { + renderID = rAF(render); + } + + trigger('move'); + + // Update SLIDEE position + if (!parallax) { + if (transform) { + $slidee[0].style[transform] = gpuAcceleration + (o.horizontal ? 'translateX' : 'translateY') + '(' + (-pos.cur) + 'px)'; + } else { + $slidee[0].style[o.horizontal ? 'left' : 'top'] = -round(pos.cur) + 'px'; + } + } + + // When animation reached the end, and dragging is not active, trigger moveEnd + if (!renderID && dragging.released) { + trigger('moveEnd'); + } + + syncScrollbar(); + } + + /** + * Synchronizes scrollbar with the SLIDEE. + * + * @return {Void} + */ + function syncScrollbar() { + if ($handle.length) { + hPos.cur = pos.start === pos.end ? 0 : (((dragging.init && !dragging.slidee) ? pos.dest : pos.cur) - pos.start) / (pos.end - pos.start) * hPos.end; + hPos.cur = within(round(hPos.cur), hPos.start, hPos.end); + if (last.hPos !== hPos.cur) { + last.hPos = hPos.cur; + if (transform) { + $handle[0].style[transform] = gpuAcceleration + (o.horizontal ? 'translateX' : 'translateY') + '(' + hPos.cur + 'px)'; + } else { + $handle[0].style[o.horizontal ? 'left' : 'top'] = hPos.cur + 'px'; + } + } + } + } + + /** + * Synchronizes pagesbar with SLIDEE. + * + * @return {Void} + */ + function syncPagesbar() { + if ($pages[0] && last.page !== rel.activePage) { + last.page = rel.activePage; + $pages.removeClass(o.activeClass).eq(rel.activePage).addClass(o.activeClass); + trigger('activePage', last.page); + } + } + + /** + * Returns the position object. + * + * @param {Mixed} item + * + * @return {Object} + */ + self.getPos = function (item) { + if (itemNav) { + var index = getIndex(item); + return index !== -1 ? items[index] : false; + } else { + var $item = $slidee.find(item).eq(0); + + if ($item[0]) { + var offset = o.horizontal ? $item.offset().left - $slidee.offset().left : $item.offset().top - $slidee.offset().top; + var size = $item[o.horizontal ? 'outerWidth' : 'outerHeight'](); + + return { + start: offset, + center: offset - frameSize / 2 + size / 2, + end: offset - frameSize + size, + size: size + }; + } else { + return false; + } + } + }; + + /** + * Continuous move in a specified direction. + * + * @param {Bool} forward True for forward movement, otherwise it'll go backwards. + * @param {Int} speed Movement speed in pixels per frame. Overrides options.moveBy value. + * + * @return {Void} + */ + self.moveBy = function (speed) { + move.speed = speed; + // If already initiated, or there is nowhere to move, abort + if (dragging.init || !move.speed || pos.cur === (move.speed > 0 ? pos.end : pos.start)) { + return; + } + // Initiate move object + move.lastTime = +new Date(); + move.startPos = pos.cur; + // Set dragging as initiated + continuousInit('button'); + dragging.init = 1; + // Start movement + trigger('moveStart'); + cAF(continuousID); + moveLoop(); + }; + + /** + * Continuous movement loop. + * + * @return {Void} + */ + function moveLoop() { + // If there is nowhere to move anymore, stop + if (!move.speed || pos.cur === (move.speed > 0 ? pos.end : pos.start)) { + self.stop(); + } + // Request new move loop if it hasn't been stopped + continuousID = dragging.init ? rAF(moveLoop) : 0; + // Update move object + move.now = +new Date(); + move.pos = pos.cur + (move.now - move.lastTime) / 1000 * move.speed; + // Slide + slideTo(dragging.init ? move.pos : round(move.pos)); + // Normally, this is triggered in render(), but if there + // is nothing to render, we have to do it manually here. + if (!dragging.init && pos.cur === pos.dest) { + trigger('moveEnd'); + } + // Update times for future iteration + move.lastTime = move.now; + } + + /** + * Stops continuous movement. + * + * @return {Void} + */ + self.stop = function () { + if (dragging.source === 'button') { + dragging.init = 0; + dragging.released = 1; + } + }; + + /** + * Activate previous item. + * + * @return {Void} + */ + self.prev = function () { + self.activate(rel.activeItem == null ? 0 : rel.activeItem - 1); + }; + + /** + * Activate next item. + * + * @return {Void} + */ + self.next = function () { + self.activate(rel.activeItem == null ? 0 : rel.activeItem + 1); + }; + + /** + * Activate previous page. + * + * @return {Void} + */ + self.prevPage = function () { + self.activatePage(rel.activePage - 1); + }; + + /** + * Activate next page. + * + * @return {Void} + */ + self.nextPage = function () { + self.activatePage(rel.activePage + 1); + }; + + /** + * Slide SLIDEE by amount of pixels. + * + * @param {Int} delta Pixels/Items. Positive means forward, negative means backward. + * @param {Bool} immediate Reposition immediately without an animation. + * + * @return {Void} + */ + self.slideBy = function (delta, immediate) { + if (!delta) { + return; + } + if (itemNav) { + self[centeredNav ? 'toCenter' : 'toStart']( + within((centeredNav ? rel.centerItem : rel.firstItem) + o.scrollBy * delta, 0, items.length) + ); + } else { + slideTo(pos.dest + delta, immediate); + } + }; + + /** + * Animate SLIDEE to a specific position. + * + * @param {Int} pos New position. + * @param {Bool} immediate Reposition immediately without an animation. + * + * @return {Void} + */ + self.slideTo = function (pos, immediate) { + slideTo(pos, immediate); + }; + + /** + * Core method for handling `toLocation` methods. + * + * @param {String} location + * @param {Mixed} item + * @param {Bool} immediate + * + * @return {Void} + */ + function to(location, item, immediate) { + // Optional arguments logic + if (type(item) === 'boolean') { + immediate = item; + item = undefined; + } + + if (item === undefined) { + slideTo(pos[location], immediate); + } else { + // You can't align items to sides of the frame + // when centered navigation type is enabled + if (centeredNav && location !== 'center') { + return; + } + + var itemPos = self.getPos(item); + if (itemPos) { + slideTo(itemPos[location], immediate, !centeredNav); + } + } + } + + /** + * Animate element or the whole SLIDEE to the start of the frame. + * + * @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE. + * @param {Bool} immediate Reposition immediately without an animation. + * + * @return {Void} + */ + self.toStart = function (item, immediate) { + to('start', item, immediate); + }; + + /** + * Animate element or the whole SLIDEE to the end of the frame. + * + * @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE. + * @param {Bool} immediate Reposition immediately without an animation. + * + * @return {Void} + */ + self.toEnd = function (item, immediate) { + to('end', item, immediate); + }; + + /** + * Animate element or the whole SLIDEE to the center of the frame. + * + * @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE. + * @param {Bool} immediate Reposition immediately without an animation. + * + * @return {Void} + */ + self.toCenter = function (item, immediate) { + to('center', item, immediate); + }; + + /** + * Get the index of an item in SLIDEE. + * + * @param {Mixed} item Item DOM element. + * + * @return {Int} Item index, or -1 if not found. + */ + function getIndex(item) { + return item != null ? + isNumber(item) ? + item >= 0 && item < items.length ? item : -1 : + $items.index(item) : + -1; + } + // Expose getIndex without lowering the compressibility of it, + // as it is used quite often throughout Sly. + self.getIndex = getIndex; + + /** + * Get index of an item in SLIDEE based on a variety of input types. + * + * @param {Mixed} item DOM element, positive or negative integer. + * + * @return {Int} Item index, or -1 if not found. + */ + function getRelativeIndex(item) { + return getIndex(isNumber(item) && item < 0 ? item + items.length : item); + } + + /** + * Activates an item. + * + * @param {Mixed} item Item DOM element, or index starting at 0. + * + * @return {Mixed} Activated item index or false on fail. + */ + function activate(item, force) { + var index = getIndex(item); + + if (!itemNav || index < 0) { + return false; + } + + // Update classes, last active index, and trigger active event only when there + // has been a change. Otherwise just return the current active index. + if (last.active !== index || force) { + // Update classes + $items.eq(rel.activeItem).removeClass(o.activeClass); + $items.eq(index).addClass(o.activeClass); + + last.active = rel.activeItem = index; + + updateButtonsState(); + trigger('active', index); + } + + return index; + } + + /** + * Activates an item and helps with further navigation when o.smart is enabled. + * + * @param {Mixed} item Item DOM element, or index starting at 0. + * @param {Bool} immediate Whether to reposition immediately in smart navigation. + * + * @return {Void} + */ + self.activate = function (item, immediate) { + var index = activate(item); + + // Smart navigation + if (o.smart && index !== false) { + // When centeredNav is enabled, center the element. + // Otherwise, determine where to position the element based on its current position. + // If the element is currently on the far end side of the frame, assume that user is + // moving forward and animate it to the start of the visible frame, and vice versa. + if (centeredNav) { + self.toCenter(index, immediate); + } else if (index >= rel.lastItem) { + self.toStart(index, immediate); + } else if (index <= rel.firstItem) { + self.toEnd(index, immediate); + } else { + resetCycle(); + } + } + }; + + /** + * Activates a page. + * + * @param {Int} index Page index, starting from 0. + * @param {Bool} immediate Whether to reposition immediately without animation. + * + * @return {Void} + */ + self.activatePage = function (index, immediate) { + if (isNumber(index)) { + slideTo(pages[within(index, 0, pages.length - 1)], immediate); + } + }; + + /** + * Return relative positions of items based on their visibility within FRAME. + * + * @param {Int} slideePos Position of SLIDEE. + * + * @return {Void} + */ + function getRelatives(slideePos) { + slideePos = within(isNumber(slideePos) ? slideePos : pos.dest, pos.start, pos.end); + + var relatives = {}; + var centerOffset = forceCenteredNav ? 0 : frameSize / 2; + + // Determine active page + if (!parallax) { + for (var p = 0, pl = pages.length; p < pl; p++) { + if (slideePos >= pos.end || p === pages.length - 1) { + relatives.activePage = pages.length - 1; + break; + } + + if (slideePos <= pages[p] + centerOffset) { + relatives.activePage = p; + break; + } + } + } + + // Relative item indexes + if (itemNav) { + var first = false; + var last = false; + var center = false; + + // From start + for (var i = 0, il = items.length; i < il; i++) { + // First item + if (first === false && slideePos <= items[i].start + items[i].half) { + first = i; + } + + // Center item + if (center === false && slideePos <= items[i].center + items[i].half) { + center = i; + } + + // Last item + if (i === il - 1 || slideePos <= items[i].end + items[i].half) { + last = i; + break; + } + } + + // Safe assignment, just to be sure the false won't be returned + relatives.firstItem = isNumber(first) ? first : 0; + relatives.centerItem = isNumber(center) ? center : relatives.firstItem; + relatives.lastItem = isNumber(last) ? last : relatives.centerItem; + } + + return relatives; + } + + /** + * Update object with relative positions. + * + * @param {Int} newPos + * + * @return {Void} + */ + function updateRelatives(newPos) { + $.extend(rel, getRelatives(newPos)); + } + + /** + * Disable navigation buttons when needed. + * + * Adds disabledClass, and when the button is