diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..19daba8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.project +*~ +*.diff +*.patch +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..0e50a3a --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +jQuery Mobile Pagination Plugin +===== +A jQuery Mobile plugin for sequential pagination between pages with support for touch, mouse, and keyboard! + +Simply add this plugin to your page and link together documents via ordinary HTML anchors. jQuery Mobile Pagination will enhance those links with touch-drag navigation in browsers that support touch events. + +This is implemented on top of jQuery Mobile's Ajax Navigation Model, meaning this plugin ties into your browser's history, so back and forward buttons work as expected!

+ + +Demos and documentation +=================================== + +This plugin requires jQuery and jQuery Mobile. It doesn't require the whole framework though, we'll document that later! + +To use: + +1. Reference jquery.mobile.pagination.css and jquery.mobile.pagination.js from your page. +2. Place the following markup somewhere inside each document that you want to make draggable. The links should point to the next and previous pages. \ No newline at end of file diff --git a/demo/2.html b/demo/2.html new file mode 100644 index 0000000..a80cc77 --- /dev/null +++ b/demo/2.html @@ -0,0 +1,21 @@ + + + + + + jQM Pagination Demo - 2 + + + + + + + + + Wooden boat on a canal + + + \ No newline at end of file diff --git a/demo/3.html b/demo/3.html new file mode 100644 index 0000000..56835d1 --- /dev/null +++ b/demo/3.html @@ -0,0 +1,21 @@ + + + + + + jQM Pagination Demo - 3 + + + + + + + + + A house on a canal in Amsterdam + + + \ No newline at end of file diff --git a/demo/4.html b/demo/4.html new file mode 100644 index 0000000..9025c2a --- /dev/null +++ b/demo/4.html @@ -0,0 +1,21 @@ + + + + + + jQM Pagination Demo - 4 + + + + + + + + + A textured wall in Amsterdam + + + \ No newline at end of file diff --git a/demo/_img/1.jpg b/demo/_img/1.jpg new file mode 100644 index 0000000..9c37cc0 Binary files /dev/null and b/demo/_img/1.jpg differ diff --git a/demo/_img/2.jpg b/demo/_img/2.jpg new file mode 100644 index 0000000..96648b0 Binary files /dev/null and b/demo/_img/2.jpg differ diff --git a/demo/_img/3.jpg b/demo/_img/3.jpg new file mode 100644 index 0000000..80e4a68 Binary files /dev/null and b/demo/_img/3.jpg differ diff --git a/demo/_img/4.jpg b/demo/_img/4.jpg new file mode 100644 index 0000000..eaca437 Binary files /dev/null and b/demo/_img/4.jpg differ diff --git a/demo/demo.css b/demo/demo.css new file mode 100644 index 0000000..1865755 --- /dev/null +++ b/demo/demo.css @@ -0,0 +1,7 @@ +body, .ui-page { + text-align: center; + background: #000; +} +img { + max-width: 100%; +} \ No newline at end of file diff --git a/demo/index.html b/demo/index.html new file mode 100644 index 0000000..fda8ee3 --- /dev/null +++ b/demo/index.html @@ -0,0 +1,21 @@ + + + + + + jQM Pagination Demo - 1 + + + + + + + + + Clog with flowers planted in it + + + \ No newline at end of file diff --git a/jquery.mobile.pagination.css b/jquery.mobile.pagination.css new file mode 100644 index 0000000..c662f88 --- /dev/null +++ b/jquery.mobile.pagination.css @@ -0,0 +1,73 @@ +/*! +* jQuery Mobile Framework : drag pagination plugin +* Copyright (c) Filament Group, Inc +* Authored by Scott Jehl, scott@filamentgroup.com +* Dual licensed under the MIT or GPL Version 2 licenses. +*/ +.ui-pagination { + left: 0; + width: 100%; +} +.ui-pagination, .ui-pagination li { + list-style: none; + margin: 0; + padding: 0; + top: 0; + position: fixed; +} +.ui-pagination li { + overflow: hidden; + width: 2.5em; + height: 4.5em; +} +.ui-pagination li a { + padding: .7em .4em .7em .45em; + opacity: .7; + position: absolute; + z-index: 999; +} +.ui-pagination .ui-pagination-prev { + left: 0; + top: 150px; +} +.ui-pagination .ui-pagination-next { + top: 150px; + right: 0; +} +.ui-pagination .ui-pagination-prev a { + right: 0; + padding-left: 1.45em; +} +.ui-pagination .ui-pagination-next a { + left: 0; + padding-right: 1.45em; +} +.ui-page.ui-page-prev { + left: -100%; + display: block; + overflow: visible; +} +.ui-page.ui-page-next { + left: 100%; + display: block; + overflow: visible; +} +.ui-pagination-dragging, +.ui-pagination-dragging .ui-page, +.ui-pagination-snapping, +.ui-pagination-snapping .ui-page { + width: 100%; +} +/* NOTE: comment this part out if your page is element-heavy!! */ +.ui-page * { + -webkit-transform: rotateY(0deg); + -moz-transform: rotateY(0deg); + transform: rotateY(0deg); +} +.ui-pagination-snapping .ui-page { + -webkit-transition: -webkit-transform .3s ease-out; + -moz-transition: -moz-transform .3s ease-out; + -ms-transition: -moz-transform .3s ease-out; + -o-transition: -o-transform .3s ease-out; + transition: transform .3s ease-out; +} \ No newline at end of file diff --git a/jquery.mobile.pagination.js b/jquery.mobile.pagination.js new file mode 100644 index 0000000..75eff9d --- /dev/null +++ b/jquery.mobile.pagination.js @@ -0,0 +1,247 @@ +/*! +* jQuery Mobile Framework : drag pagination plugin +* Copyright (c) Filament Group, Inc +* Authored by Scott Jehl, scott@filamentgroup.com +* Dual licensed under the MIT or GPL Version 2 licenses. +*/ +(function( $, undefined ){ + + //auto-init on pagecreate + $( document ).bind( "pagecreate", function( e ){ + $( ":jqmData(role='pagination')", e.target ).pagination(); + }); + + //create widget + $.widget( "mobile.pagination", $.mobile.widget, { + _create: function() { + var $el = this.element, + $page = $el.closest( ".ui-page" ), + $links = $el.find( "a" ), + $origin = $.mobile.pageContainer, + classNS = "ui-pagination", + prevLIClass = classNS + "-prev", + nextLIClass = classNS + "-next", + prevPClass = "ui-page-prev", + nextPClass = "ui-page-next", + snapClass = classNS + "-snapping", + dragClass = classNS + "-dragging", + dragClassOn = false, + $nextPage, + $prevPage; + + $el.addClass( classNS ); + + //prefetch next and prev pages when page is first shown + $page.one( "pageshow", function(){ + $links.each(function(){ + var next = $( this ).closest( "." + nextLIClass ).length, + url = $( this ).attr( "href" ), + setNP = function( newPage ){ + if( next ){ + $nextPage = newPage; + } + else{ + $prevPage = newPage; + } + }; + + if( !$page ){ + return; + } + + //if it's a local div reference, make sure it's initialized + if( url.indexOf( "#") === 0 ){ + setNP( $( ":jqmData(url='" + url.split("#")[1] + "')").page() ); + return; + } + + if( $( ":jqmData(url='" + url + "')" ).length ){ + return; + } + + //NOTE: this must handle local # urls as well in jQM + $.mobile + .loadPage( url ) + .done(function( url, options, newPage ) { + setNP( newPage ); + }); + }); + }); + + //set up next and prev buttons + + $links.each(function(){ + var reverse = $( this ).closest( "." + prevLIClass ).length; + + $(this) + .buttonMarkup({ + "role" : "button", + "theme" : "d", + "iconpos" : "notext", + "icon" : "arrow-" + ( reverse ? "l" : "r") + }) + .bind( "vclick", function(){ + $.mobile.changePage( $(this).attr( "href" ), { reverse: reverse } ); + return false; + }); + }); + + // Keyboard handling + $( document ) + .unbind( "keyup.pagination" ) + .bind( "keyup.pagination", function( e ){ + if( !$( e.target ).is( "input, textarea, select, button" ) ){ + var targetA, reverse; + // Left arrow + if( e.keyCode === $.mobile.keyCode.LEFT ){ + targetA = $( ".ui-page-active .ui-pagination-prev a" ); + reverse = true; + } + // Right arrow + else if( e.keyCode === $.mobile.keyCode.RIGHT ){ + targetA = $( ".ui-page-active .ui-pagination-next a" ); + } + if( targetA ){ + $.mobile.changePage( targetA.attr( "href" ), { reverse: reverse, transition: targetA.jqmData( "transition" ) } ); + e.preventDefault(); + } + } + }); + + //page drag handling + $page + .bind("touchstart", function(e) { + var data = e.originalEvent.touches ? e.originalEvent.touches[0] : e, + start = [ data.pageX, data.pageY ], + $pages = $page.add( $nextPage ).add( $prevPage ), + dragStart = false, + setTransform = function( pxVal ){ + var val = "translateX(" + ( pxVal / $origin.width() * 100 ) + "%)"; + $pages.css({ + "-webkit-transform" : val, + "-moz-transform" : val, + "-ms-transform" : val, + "-o-transform" : val, + "transform" : val + }); + }, + moveHandler = function( e ) { + var data = e.originalEvent.touches ? e.originalEvent.touches[0] : e, + stop = [ data.pageX, data.pageY ], + xdist = Math.abs(start[0] - stop[0]); + + if( !dragStart ){ + dragStart = true; + $page.trigger( "dragstart.pagination" ); + } + + // prevent scrolling + if ( xdist > 8 ) { + e.preventDefault(); + } + + if( !dragClassOn ){ + dragClassOn = true; + $origin.addClass( dragClass ); + } + + $page.trigger( "dragging.pagination" ); + + setTransform( stop[0] - start[0] ); + }, + snapTo = function( newOffset, immediate ){ + var $newActive = newOffset === 0 ? $page : newOffset > 0 ? $prevPage : $nextPage, + samePage = !$newActive || $newActive.is( $page ), + newUrl = samePage && $page.jqmData( "url" ) || $newActive.jqmData( "url" ), + doneCB = function(){ + + //if it's a new page, change history! + if( !samePage ){ + //remove active state on old active + $page.removeClass( $.mobile.activePageClass ); + + //disable hash listening + $.mobile.urlHistory.ignoreNextHashChange = true; + + $.mobile.path.set( newUrl ); + + //if title element wasn't found, try the page div data attr too + var newPageTitle = $newActive.jqmData( "title" ) || $newActive.children( ":jqmData(role='header')" ).find( ".ui-title" ).text(); + if( !!newPageTitle ) { + pageTitle = newPageTitle; + } + + //add page to history stack if it's not back or forward + $.mobile.urlHistory.addNew( newUrl, undefined, pageTitle, $newActive ); + + //set page title + document.title = $.mobile.urlHistory.getActive().title; + + //set "toPage" as activePage + $.mobile.activePage = $newActive; + + $page.jqmData( "page" )._trigger( "hide", null, { nextPage: $newActive } ); + $newActive.jqmData( "page" )._trigger( "show", null, { prevPage: $page } ); + } + + $origin.removeClass( snapClass + " " + dragClass ); + + dragClassOn = dragStart = false; + + $page.trigger( "snapstop.pagination" ); + + $pages + .removeClass( prevPClass + " " + nextPClass ) + .removeAttr( "style" ); + } + + if( !samePage ){ + $page.jqmData( "page" )._trigger( "beforehide", null, { nextPage: $newActive } ); + //switch active page + $newActive + .addClass( $.mobile.activePageClass ) + .jqmData( "page" )._trigger( "beforeshow", null, { prevPage: $page } ); + } + + if( immediate ){ + $page.trigger( "snapping.pagination" ); + setTransform( newOffset ); + doneCB(); + } + else{ + $page.trigger( "snapping.pagination" ); + $origin.addClass( snapClass ); + //switch to animation complete handler + $page.one( "webkitTransitionEnd oTransitionEnd transitionend", doneCB ); + setTransform( newOffset ); + } + + } + stop; + + //line up the pages + if( $nextPage ){ + $nextPage.addClass( nextPClass ); + } + if( $prevPage ){ + $prevPage.addClass( prevPClass ); + } + + //bind touch handlers + $page + .bind( "gesturestart.pagination touchend.pagination", function(){ + $page.unbind( ".pagination" ); + }) + .bind( "touchmove.pagination", moveHandler ) + .one( "touchend", function( e ){ + var pOffset = $page.offset().left, + absOS = Math.abs( pOffset ), + toGo = $page.width() - absOS; + + snapTo( absOS > 150 ? pOffset + ( pOffset > 0 ? toGo : -toGo ) : 0, absOS < 10 ); + }); + }); + } + }); + +}( jQuery )); \ No newline at end of file