diff options
| author | PliablePixels <pliablepixels@gmail.com> | 2015-07-24 15:48:01 -0400 |
|---|---|---|
| committer | PliablePixels <pliablepixels@gmail.com> | 2015-07-24 15:48:01 -0400 |
| commit | 83400033a3b7a91ad072a5d306355c9cd5a80d82 (patch) | |
| tree | b84d23a607523249554dc97ed26f000ca03d0abd /www/lib | |
| parent | 89640e9b0212a2525ea132b1d11bb8962f5444dd (diff) | |
integrated event scrubbing with direct image access - need to clean up code
Diffstat (limited to 'www/lib')
53 files changed, 8805 insertions, 0 deletions
diff --git a/www/lib/angular-carousel/.bower.json b/www/lib/angular-carousel/.bower.json new file mode 100644 index 00000000..fe2bba10 --- /dev/null +++ b/www/lib/angular-carousel/.bower.json @@ -0,0 +1,36 @@ +{ + "name": "angular-carousel", + "description": "Angular Carousel - Mobile friendly touch carousel for AngularJS", + "version": "0.3.12", + "homepage": "http://revolunet.github.com/angular-carousel", + "author": "Julien Bouquillon <julien@revolunet.com>", + "repository": { + "type": "git", + "url": "git://github.com/revolunet/angular-carousel.git" + }, + "main": [ + "dist/angular-carousel.js", + "dist/angular-carousel.css" + ], + "ignore": [ + "demo" + ], + "dependencies": { + "angular": ">=1.2.10", + "angular-touch": ">=1.2.10" + }, + "devDependencies": { + "angular-mocks": ">=1.2.10", + "requirejs": ">=2.1.0" + }, + "_release": "0.3.12", + "_resolution": { + "type": "version", + "tag": "0.3.12", + "commit": "12f28ed1de9d78b4c1f9cf58ca71cdf4a8452643" + }, + "_source": "git://github.com/revolunet/angular-carousel.git", + "_target": "~0.3.12", + "_originalSource": "angular-carousel", + "_direct": true +} diff --git a/www/lib/angular-carousel/.bowerrc b/www/lib/angular-carousel/.bowerrc new file mode 100644 index 00000000..11aa4a12 --- /dev/null +++ b/www/lib/angular-carousel/.bowerrc @@ -0,0 +1,4 @@ +{ + "directory": "bower_components", + "json": "bower.json" +} diff --git a/www/lib/angular-carousel/.editorconfig b/www/lib/angular-carousel/.editorconfig new file mode 100644 index 00000000..c6c8b362 --- /dev/null +++ b/www/lib/angular-carousel/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/www/lib/angular-carousel/.gitignore b/www/lib/angular-carousel/.gitignore new file mode 100644 index 00000000..90b4e226 --- /dev/null +++ b/www/lib/angular-carousel/.gitignore @@ -0,0 +1,6 @@ +.sass-cache +*.DS_Store +node_modules +bower_components +coverage +.idea
\ No newline at end of file diff --git a/www/lib/angular-carousel/Gruntfile.js b/www/lib/angular-carousel/Gruntfile.js new file mode 100755 index 00000000..e86f7a80 --- /dev/null +++ b/www/lib/angular-carousel/Gruntfile.js @@ -0,0 +1,153 @@ +/* global require, module, process, __dirname */ + +'use strict'; + +var path = require('path'); + +module.exports = function(grunt) { + + require('load-grunt-tasks')(grunt); + + // Project configuration. + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + meta: { + banner: '/**\n' + + ' * <%= pkg.description %>\n' + + ' * @version v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' + + ' * @link <%= pkg.homepage %>\n' + + ' * @author <%= pkg.author %>\n' + + ' * @license MIT License, http://www.opensource.org/licenses/MIT\n' + + ' */\n' + }, + connect: { + devserver: { + options: { + port: 9999, + hostname: '0.0.0.0', + base: '.' + } + } + }, + dirs: { + src: 'src', + dest: 'dist' + }, + copy: { + + }, + autoprefixer: { + source: { + //options: { + //browsers: ['last 2 version'] + //}, + src: '<%= dirs.dest %>/<%= pkg.name %>.css', + dest: '<%= dirs.dest %>/<%= pkg.name %>.css' + } + }, + concat: { + options: { + banner: '<%= meta.banner %>' + }, + dist: { + src: ['<%= dirs.src %>/*.js', '<%= dirs.src %>/**/*.js'], + dest: '<%= dirs.dest %>/<%= pkg.name %>.js' + } + }, + + sass: { + dist: { + files: [{ + expand: true, + cwd: './src/css', + src: ['*.scss'], + dest: './dist', + ext: '.css' + }] + } + }, + + cssmin: { + combine: { + files: { + '<%= dirs.dest %>/<%= pkg.name %>.min.css': ['<%= dirs.dest %>/<%= pkg.name %>.css'] + } + } + }, + ngAnnotate: { + dist: { + files: { + '<%= concat.dist.dest %>': ['<%= concat.dist.dest %>'] + } + } + }, + uglify: { + options: { + banner: '<%= meta.banner %>' + }, + dist: { + src: ['<%= concat.dist.dest %>'], + dest: '<%= dirs.dest %>/<%= pkg.name %>.min.js' + } + }, + jshint: { + files: ['Gruntfile.js', '<%= dirs.src %>/*.js', 'test/unit/*.js'], + options: { + curly: false, + browser: true, + eqeqeq: true, + immed: true, + latedef: true, + newcap: true, + noarg: true, + sub: true, + undef: true, + boss: true, + eqnull: true, + expr: true, + node: true, + globals: { + exports: true, + angular: false, + $: false + } + } + }, + karma: { + options: { + // needed to use absolute path for some reason + configFile: path.join(__dirname, 'test', 'karma.conf.js') + }, + unit: { + port: 7101, + singleRun: false, + background: true + }, + continuous: { + singleRun: true + } + }, + changelog: { + options: { + dest: 'CHANGELOG.md' + } + }, + watch: { + dev: { + files: ['<%= dirs.src %>/**'], + tasks: ['build', 'karma:unit:run'] + }, + test: { + files: ['test/unit/**'], + tasks: ['karma:unit:run'] + } + } + }); + + // Build task. + grunt.registerTask('build', ['jshint', 'concat', 'ngAnnotate', 'uglify', 'sass', 'autoprefixer', 'cssmin']); + + // Default task. + grunt.registerTask('default', ['build', 'connect', 'karma:unit', 'watch']); + +}; diff --git a/www/lib/angular-carousel/README.md b/www/lib/angular-carousel/README.md new file mode 100644 index 00000000..a50acbc9 --- /dev/null +++ b/www/lib/angular-carousel/README.md @@ -0,0 +1,90 @@ +# AngularJS Touch Carousel + +An AngularJS carousel implementation optimised for mobile devices. + +Demo : http://revolunet.github.io/angular-carousel + +Comments and contributions welcome :) + +Proudly brought to you by the [@revolunet](http://twitter.com/revolunet) team. + + +## Usage : + + - If you use bower, just `bower install angular-carousel`. If not, download files [from the github repo](./dist) + - Add `angular-touch.js`, `angular-carousel.css`, and `angular-carousel.js` to your code: +```html +<link href="angular-carousel.css" rel="stylesheet" type="text/css" /> +<script src="angular.js"></script> +<script src="angular-touch.js"></script> +<script src="angular-carousel.js"></script> +``` + - Add a dependency to the `angular-carousel` module in your application. +```js +angular.module('MyApp', ['angular-carousel']); +``` + + - Add a `rn-carousel` attribute to your `<ul>` block and your `<li>`'s become magically swipable ;) +```html +<ul rn-carousel class="image"> + <li ng-repeat="image in sportImages"> + <div class="layer">{{ image }}</div> + </li> +</ul> +``` + + - You can also use `rn-carousel` without ng-repeat ;) +```html +<ul rn-carousel class="image"> + <li>slide #1</li> + <li>slide #2</li> + <li>slide #3</li> +</ul> +``` + +## Directive options : + - `rn-carousel-index` two way binding integer to control the carousel position (0-indexed) + - `rn-carousel-buffered` add this attribute to enable the carousel buffering, good to minimize the DOM (5 slides) + - `rn-carousel-controls` add this attribute to enable builtin prev/next buttons (you can override by CSS) + - `rn-carousel-auto-slide` add this attribute to make the carousel slide automatically after given seconds (default=3) + - `rn-carousel-transition` : transition type, can be one of `slide, zoom, hexagon, fadeAndSlide, none`. (default=slide) + - `rn-carousel-locked`: two way binding boolean that lock/unlock the carousel + - `rn-carousel-deep-watch`: Deep watch the collection which enable to dynamically add slides at beginning without corrupting position + - `rn-carousel-easing`: add this attritube to specify a formula for easing, these can be found in the [shifty + library](https://github.com/jeremyckahn/shifty/blob/master/src/shifty.formulas.js) (default=easeIn) + - `rn-carousel-duration`: add this attritube to set the duration of the transition (default=300) + +## Indicators + +You can add position indicators by adding this directive where you want : +```html +<div rn-carousel-indicators ng-if="slides.length > 1" slides="slides" rn-carousel-index="carouselIndex"></div> +``` + - `slides` is the same collection you use in the carousel ng-repeat + - `carouselIndex` is the same index you've defined for the carousel + +## Notes : + - if you use IE<=9, iOS<7 or Android<4 please include the [requestAnimationFrame polyfill](https://github.com/darius/requestAnimationFrame/blob/master/requestAnimationFrame.js) in your application. + - if you use IE<=8 include the [es5-shim polyfill](https://github.com/es-shims/es5-shim/blob/master/es5-shim.min.js) in your application. + - don't set any style attribute to your li's. they would be overwritten by the carousel (use classes instead). + - angular-carousel use the great [shifty.js](https://github.com/jeremyckahn/shifty) for the animations + +## Todo : + - delay autoslide on indicators click/move + - customisable transitions + - more transition types + - infinite loop support + +## Contributing + - Please follow [AngularJS GIT conventions](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#) + - Please add tests + - Please update the README and demo (index.html) + +## Inspirations + - https://github.com/ajoslin/angular-mobile-nav + - http://mobile.smashingmagazine.com/2012/06/21/play-with-hardware-accelerated-css/ + - http://ariya.ofilabs.com/2013/08/javascript-kinetic-scrolling-part-1.html + - Thanks to all angular folks for all the tips :) + +## License +As AngularJS itself, this module is released under the permissive [MIT license](http://revolunet.mit-license.org). Your contributions are always welcome. diff --git a/www/lib/angular-carousel/bower.json b/www/lib/angular-carousel/bower.json new file mode 100644 index 00000000..289b5926 --- /dev/null +++ b/www/lib/angular-carousel/bower.json @@ -0,0 +1,26 @@ +{ + "name": "angular-carousel", + "description": "Angular Carousel - Mobile friendly touch carousel for AngularJS", + "version": "0.3.12", + "homepage": "http://revolunet.github.com/angular-carousel", + "author": "Julien Bouquillon <julien@revolunet.com>", + "repository": { + "type": "git", + "url": "git://github.com/revolunet/angular-carousel.git" + }, + "main": [ + "dist/angular-carousel.js", + "dist/angular-carousel.css" + ], + "ignore": [ + "demo" + ], + "dependencies": { + "angular": ">=1.2.10", + "angular-touch": ">=1.2.10" + }, + "devDependencies": { + "angular-mocks": ">=1.2.10", + "requirejs": ">=2.1.0" + } +} diff --git a/www/lib/angular-carousel/dist/angular-carousel.css b/www/lib/angular-carousel/dist/angular-carousel.css new file mode 100755 index 00000000..a11d8523 --- /dev/null +++ b/www/lib/angular-carousel/dist/angular-carousel.css @@ -0,0 +1,64 @@ +input[type=range] { + width: 300px; } + +ul[rn-carousel] { + overflow: hidden; + padding: 0; + white-space: nowrap; + position: relative; + -webkit-perspective: 1000px; + -ms-perspective: 1000px; + perspective: 1000px; + -ms-touch-action: pan-y; + touch-action: pan-y; } + ul[rn-carousel] > li { + color: black; + -webkit-backface-visibility: hidden; + -ms-backface-visibility: hidden; + backface-visibility: hidden; + overflow: visible; + vertical-align: top; + position: absolute; + left: 0; + right: 0; + white-space: normal; + padding: 0; + margin: 0; + list-style-type: none; + width: 100%; + height: 100%; + display: inline-block; } + +/* prevent flickering when moving buffer */ +ul[rn-carousel-buffered] > li { + display: none; } + +ul[rn-carousel-transition="hexagon"] { + overflow: visible; } + +/* indicators */ +div.rn-carousel-indicator span { + cursor: pointer; + color: #666; } + div.rn-carousel-indicator span.active { + color: white; } + +/* prev/next controls */ +.rn-carousel-control { + -webkit-transition: opacity 0.2s ease-out; + transition: opacity 0.2s ease-out; + font-size: 2rem; + position: absolute; + top: 40%; + opacity: 0.75; + cursor: pointer; } + .rn-carousel-control:hover { + opacity: 1; } + .rn-carousel-control.rn-carousel-control-prev { + left: 0.5em; } + .rn-carousel-control.rn-carousel-control-prev:before { + content: "<"; } + .rn-carousel-control.rn-carousel-control-next { + right: 0.5em; } + .rn-carousel-control.rn-carousel-control-next:before { + content: ">"; } diff --git a/www/lib/angular-carousel/dist/angular-carousel.css.map b/www/lib/angular-carousel/dist/angular-carousel.css.map new file mode 100644 index 00000000..835dca75 --- /dev/null +++ b/www/lib/angular-carousel/dist/angular-carousel.css.map @@ -0,0 +1,7 @@ +{ +"version": 3, +"mappings": "AAAA,iBAAkB;EAChB,KAAK,EAAC,KAAK;;AAGb,eAAgB;EACd,QAAQ,EAAC,MAAM;EACf,OAAO,EAAC,CAAC;EACT,WAAW,EAAE,MAAM;EACnB,QAAQ,EAAE,QAAQ;EAClB,WAAW,EAAC,MAAM;EAClB,gBAAgB,EAAE,KAAK;EACvB,YAAY,EAAE,KAAK;EACnB,oBAAK;IACH,KAAK,EAAC,KAAK;IACX,mBAAmB,EAAE,MAAM;IAC3B,QAAQ,EAAE,OAAO;IACjB,cAAc,EAAE,GAAG;IACnB,QAAQ,EAAC,QAAQ;IACjB,IAAI,EAAC,CAAC;IACN,KAAK,EAAC,CAAC;IACP,WAAW,EAAE,MAAM;IACnB,OAAO,EAAC,CAAC;IACT,MAAM,EAAC,CAAC;IACR,eAAe,EAAC,IAAI;IACpB,KAAK,EAAC,IAAI;IACV,MAAM,EAAC,IAAI;IACX,OAAO,EAAC,YAAY;;;AAKxB,6BAA8B;EAC5B,OAAO,EAAC,IAAI;;AAGd,oCAAqC;EACnC,QAAQ,EAAC,OAAO;;;AAIlB,8BAA+B;EAC7B,MAAM,EAAC,OAAO;EACd,KAAK,EAAE,IAAI;EACX,qCAAS;IACP,KAAK,EAAE,KAAK;;;AAKhB,oBAAqB;EACnB,UAAU,EAAE,qBAAqB;EACjC,SAAS,EAAE,IAAI;EACf,QAAQ,EAAE,QAAQ;EAClB,GAAG,EAAE,GAAG;EACR,OAAO,EAAE,IAAI;EACb,MAAM,EAAE,OAAO;EACf,0BAAQ;IACN,OAAO,EAAE,CAAC;EAGZ,6CAA2B;IACzB,IAAI,EAAE,KAAK;IACX,oDAAS;MACP,OAAO,EAAE,GAAG;EAIhB,6CAA2B;IACzB,KAAK,EAAE,KAAK;IACZ,oDAAS;MACP,OAAO,EAAE,GAAG", +"sources": ["../src/css/angular-carousel.scss"], +"names": [], +"file": "angular-carousel.css" +} diff --git a/www/lib/angular-carousel/dist/angular-carousel.js b/www/lib/angular-carousel/dist/angular-carousel.js new file mode 100644 index 00000000..8d1f795d --- /dev/null +++ b/www/lib/angular-carousel/dist/angular-carousel.js @@ -0,0 +1,2097 @@ +/** + * Angular Carousel - Mobile friendly touch carousel for AngularJS + * @version v0.3.12 - 2015-06-11 + * @link http://revolunet.github.com/angular-carousel + * @author Julien Bouquillon <julien@revolunet.com> + * @license MIT License, http://www.opensource.org/licenses/MIT + */ +/*global angular */ + +/* +Angular touch carousel with CSS GPU accel and slide buffering +http://github.com/revolunet/angular-carousel + +*/ + +angular.module('angular-carousel', [ + 'ngTouch', + 'angular-carousel.shifty' +]); + +angular.module('angular-carousel') + +.directive('rnCarouselAutoSlide', ['$interval', function($interval) { + return { + restrict: 'A', + link: function (scope, element, attrs) { + var stopAutoPlay = function() { + if (scope.autoSlider) { + $interval.cancel(scope.autoSlider); + scope.autoSlider = null; + } + }; + var restartTimer = function() { + scope.autoSlide(); + }; + + scope.$watch('carouselIndex', restartTimer); + + if (attrs.hasOwnProperty('rnCarouselPauseOnHover') && attrs.rnCarouselPauseOnHover !== 'false'){ + element.on('mouseenter', stopAutoPlay); + element.on('mouseleave', restartTimer); + } + + scope.$on('$destroy', function(){ + stopAutoPlay(); + element.off('mouseenter', stopAutoPlay); + element.off('mouseleave', restartTimer); + }); + } + }; +}]); + +angular.module('angular-carousel') + +.directive('rnCarouselIndicators', ['$parse', function($parse) { + return { + restrict: 'A', + scope: { + slides: '=', + index: '=rnCarouselIndex' + }, + templateUrl: 'carousel-indicators.html', + link: function(scope, iElement, iAttributes) { + var indexModel = $parse(iAttributes.rnCarouselIndex); + scope.goToSlide = function(index) { + indexModel.assign(scope.$parent.$parent, index); + }; + } + }; +}]); + +angular.module('angular-carousel').run(['$templateCache', function($templateCache) { + $templateCache.put('carousel-indicators.html', + '<div class="rn-carousel-indicator">\n' + + '<span ng-repeat="slide in slides" ng-class="{active: $index==index}" ng-click="goToSlide($index)">●</span>' + + '</div>' + ); +}]); + +(function() { + "use strict"; + + angular.module('angular-carousel') + + .service('DeviceCapabilities', function() { + + // TODO: merge in a single function + + // detect supported CSS property + function detectTransformProperty() { + var transformProperty = 'transform', + safariPropertyHack = 'webkitTransform'; + if (typeof document.body.style[transformProperty] !== 'undefined') { + + ['webkit', 'moz', 'o', 'ms'].every(function (prefix) { + var e = '-' + prefix + '-transform'; + if (typeof document.body.style[e] !== 'undefined') { + transformProperty = e; + return false; + } + return true; + }); + } else if (typeof document.body.style[safariPropertyHack] !== 'undefined') { + transformProperty = '-webkit-transform'; + } else { + transformProperty = undefined; + } + return transformProperty; + } + + //Detect support of translate3d + function detect3dSupport() { + var el = document.createElement('p'), + has3d, + transforms = { + 'webkitTransform': '-webkit-transform', + 'msTransform': '-ms-transform', + 'transform': 'transform' + }; + // Add it to the body to get the computed style + document.body.insertBefore(el, null); + for (var t in transforms) { + if (el.style[t] !== undefined) { + el.style[t] = 'translate3d(1px,1px,1px)'; + has3d = window.getComputedStyle(el).getPropertyValue(transforms[t]); + } + } + document.body.removeChild(el); + return (has3d !== undefined && has3d.length > 0 && has3d !== "none"); + } + + return { + has3d: detect3dSupport(), + transformProperty: detectTransformProperty() + }; + + }) + + .service('computeCarouselSlideStyle', ["DeviceCapabilities", function(DeviceCapabilities) { + // compute transition transform properties for a given slide and global offset + return function(slideIndex, offset, transitionType) { + var style = { + display: 'inline-block' + }, + opacity, + absoluteLeft = (slideIndex * 100) + offset, + slideTransformValue = DeviceCapabilities.has3d ? 'translate3d(' + absoluteLeft + '%, 0, 0)' : 'translate3d(' + absoluteLeft + '%, 0)', + distance = ((100 - Math.abs(absoluteLeft)) / 100); + + if (!DeviceCapabilities.transformProperty) { + // fallback to default slide if transformProperty is not available + style['margin-left'] = absoluteLeft + '%'; + } else { + if (transitionType == 'fadeAndSlide') { + style[DeviceCapabilities.transformProperty] = slideTransformValue; + opacity = 0; + if (Math.abs(absoluteLeft) < 100) { + opacity = 0.3 + distance * 0.7; + } + style.opacity = opacity; + } else if (transitionType == 'hexagon') { + var transformFrom = 100, + degrees = 0, + maxDegrees = 60 * (distance - 1); + + transformFrom = offset < (slideIndex * -100) ? 100 : 0; + degrees = offset < (slideIndex * -100) ? maxDegrees : -maxDegrees; + style[DeviceCapabilities.transformProperty] = slideTransformValue + ' ' + 'rotateY(' + degrees + 'deg)'; + style[DeviceCapabilities.transformProperty + '-origin'] = transformFrom + '% 50%'; + } else if (transitionType == 'zoom') { + style[DeviceCapabilities.transformProperty] = slideTransformValue; + var scale = 1; + if (Math.abs(absoluteLeft) < 100) { + scale = 1 + ((1 - distance) * 2); + } + style[DeviceCapabilities.transformProperty] += ' scale(' + scale + ')'; + style[DeviceCapabilities.transformProperty + '-origin'] = '50% 50%'; + opacity = 0; + if (Math.abs(absoluteLeft) < 100) { + opacity = 0.3 + distance * 0.7; + } + style.opacity = opacity; + } else { + style[DeviceCapabilities.transformProperty] = slideTransformValue; + } + } + return style; + }; + }]) + + .service('createStyleString', function() { + return function(object) { + var styles = []; + angular.forEach(object, function(value, key) { + styles.push(key + ':' + value); + }); + return styles.join(';'); + }; + }) + + .directive('rnCarousel', ['$swipe', '$window', '$document', '$parse', '$compile', '$timeout', '$interval', 'computeCarouselSlideStyle', 'createStyleString', 'Tweenable', + function($swipe, $window, $document, $parse, $compile, $timeout, $interval, computeCarouselSlideStyle, createStyleString, Tweenable) { + // internal ids to allow multiple instances + var carouselId = 0, + // in absolute pixels, at which distance the slide stick to the edge on release + rubberTreshold = 3; + + var requestAnimationFrame = $window.requestAnimationFrame || $window.webkitRequestAnimationFrame || $window.mozRequestAnimationFrame; + + function getItemIndex(collection, target, defaultIndex) { + var result = defaultIndex; + collection.every(function(item, index) { + if (angular.equals(item, target)) { + result = index; + return false; + } + return true; + }); + return result; + } + + return { + restrict: 'A', + scope: true, + compile: function(tElement, tAttributes) { + // use the compile phase to customize the DOM + var firstChild = tElement[0].querySelector('li'), + firstChildAttributes = (firstChild) ? firstChild.attributes : [], + isRepeatBased = false, + isBuffered = false, + repeatItem, + repeatCollection; + + // try to find an ngRepeat expression + // at this point, the attributes are not yet normalized so we need to try various syntax + ['ng-repeat', 'data-ng-repeat', 'ng:repeat', 'x-ng-repeat'].every(function(attr) { + var repeatAttribute = firstChildAttributes[attr]; + if (angular.isDefined(repeatAttribute)) { + // ngRepeat regexp extracted from angular 1.2.7 src + var exprMatch = repeatAttribute.value.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/), + trackProperty = exprMatch[3]; + + repeatItem = exprMatch[1]; + repeatCollection = exprMatch[2]; + + if (repeatItem) { + if (angular.isDefined(tAttributes['rnCarouselBuffered'])) { + // update the current ngRepeat expression and add a slice operator if buffered + isBuffered = true; + repeatAttribute.value = repeatItem + ' in ' + repeatCollection + '|carouselSlice:carouselBufferIndex:carouselBufferSize'; + if (trackProperty) { + repeatAttribute.value += ' track by ' + trackProperty; + } + } + isRepeatBased = true; + return false; + } + } + return true; + }); + + return function(scope, iElement, iAttributes, containerCtrl) { + + carouselId++; + + var defaultOptions = { + transitionType: iAttributes.rnCarouselTransition || 'slide', + transitionEasing: iAttributes.rnCarouselEasing || 'easeTo', + transitionDuration: parseFloat(iAttributes.rnCarouselDuration, 10) || 300, + isSequential: true, + autoSlideDuration: 3, + bufferSize: 31, + /* in container % how much we need to drag to trigger the slide change */ + moveTreshold: 0.1, + defaultIndex: 0 + }; + + // TODO + var options = angular.extend({}, defaultOptions); + + var pressed, + startX, + isIndexBound = false, + offset = 0, + destination, + swipeMoved = false, + //animOnIndexChange = true, + currentSlides = [], + elWidth = null, + elX = null, + animateTransitions = true, + intialState = true, + animating = false, + mouseUpBound = false, + locked = false; + + //rn-swipe-disabled =true will only disable swipe events + if(iAttributes.rnSwipeDisabled !== "true") { + $swipe.bind(iElement, { + start: swipeStart, + move: swipeMove, + end: swipeEnd, + cancel: function(event) { + swipeEnd({}, event); + } + }); + } + + function getSlidesDOM() { + return iElement[0].querySelectorAll('ul[rn-carousel] > li'); + } + + function documentMouseUpEvent(event) { + // in case we click outside the carousel, trigger a fake swipeEnd + swipeMoved = true; + swipeEnd({ + x: event.clientX, + y: event.clientY + }, event); + } + + function updateSlidesPosition(offset) { + // manually apply transformation to carousel childrens + // todo : optim : apply only to visible items + var x = scope.carouselBufferIndex * 100 + offset; + angular.forEach(getSlidesDOM(), function(child, index) { + child.style.cssText = createStyleString(computeCarouselSlideStyle(index, x, options.transitionType)); + }); + } + + scope.nextSlide = function(slideOptions) { + var index = scope.carouselIndex + 1; + if (index > currentSlides.length - 1) { + index = 0; + } + if (!locked) { + goToSlide(index, slideOptions); + } + }; + + scope.prevSlide = function(slideOptions) { + var index = scope.carouselIndex - 1; + if (index < 0) { + index = currentSlides.length - 1; + } + goToSlide(index, slideOptions); + }; + + function goToSlide(index, slideOptions) { + //console.log('goToSlide', arguments); + // move a to the given slide index + if (index === undefined) { + index = scope.carouselIndex; + } + + slideOptions = slideOptions || {}; + if (slideOptions.animate === false || options.transitionType === 'none') { + locked = false; + offset = index * -100; + scope.carouselIndex = index; + updateBufferIndex(); + return; + } + + locked = true; + var tweenable = new Tweenable(); + tweenable.tween({ + from: { + 'x': offset + }, + to: { + 'x': index * -100 + }, + duration: options.transitionDuration, + easing: options.transitionEasing, + step: function(state) { + updateSlidesPosition(state.x); + }, + finish: function() { + scope.$apply(function() { + scope.carouselIndex = index; + offset = index * -100; + updateBufferIndex(); + $timeout(function () { + locked = false; + }, 0, false); + }); + } + }); + } + + function getContainerWidth() { + var rect = iElement[0].getBoundingClientRect(); + return rect.width ? rect.width : rect.right - rect.left; + } + + function updateContainerWidth() { + elWidth = getContainerWidth(); + } + + function bindMouseUpEvent() { + if (!mouseUpBound) { + mouseUpBound = true; + $document.bind('mouseup', documentMouseUpEvent); + } + } + + function unbindMouseUpEvent() { + if (mouseUpBound) { + mouseUpBound = false; + $document.unbind('mouseup', documentMouseUpEvent); + } + } + + function swipeStart(coords, event) { + // console.log('swipeStart', coords, event); + if (locked || currentSlides.length <= 1) { + return; + } + updateContainerWidth(); + elX = iElement[0].querySelector('li').getBoundingClientRect().left; + pressed = true; + startX = coords.x; + return false; + } + + function swipeMove(coords, event) { + //console.log('swipeMove', coords, event); + var x, delta; + bindMouseUpEvent(); + if (pressed) { + x = coords.x; + delta = startX - x; + if (delta > 2 || delta < -2) { + swipeMoved = true; + var moveOffset = offset + (-delta * 100 / elWidth); + updateSlidesPosition(moveOffset); + } + } + return false; + } + + var init = true; + scope.carouselIndex = 0; + + if (!isRepeatBased) { + // fake array when no ng-repeat + currentSlides = []; + angular.forEach(getSlidesDOM(), function(node, index) { + currentSlides.push({id: index}); + }); + } + + if (iAttributes.rnCarouselControls!==undefined) { + // dont use a directive for this + var nextSlideIndexCompareValue = isRepeatBased ? repeatCollection.replace('::', '') + '.length - 1' : currentSlides.length - 1; + var tpl = '<div class="rn-carousel-controls">\n' + + ' <span class="rn-carousel-control rn-carousel-control-prev" ng-click="prevSlide()" ng-if="carouselIndex > 0"></span>\n' + + ' <span class="rn-carousel-control rn-carousel-control-next" ng-click="nextSlide()" ng-if="carouselIndex < ' + nextSlideIndexCompareValue + '"></span>\n' + + '</div>'; + iElement.parent().append($compile(angular.element(tpl))(scope)); + } + + if (iAttributes.rnCarouselAutoSlide!==undefined) { + var duration = parseFloat(iAttributes.rnCarouselAutoSlide, 10) || options.autoSlideDuration; + scope.autoSlide = function() { + if (scope.autoSlider) { + $interval.cancel(scope.autoSlider); + scope.autoSlider = null; + } + scope.autoSlider = $interval(function() { + if (!locked && !pressed) { + scope.nextSlide(); + } + }, duration * 1000); + }; + } + + if (iAttributes.rnCarouselDefaultIndex) { + var defaultIndexModel = $parse(iAttributes.rnCarouselDefaultIndex); + options.defaultIndex = defaultIndexModel(scope.$parent) || 0; + } + + if (iAttributes.rnCarouselIndex) { + var updateParentIndex = function(value) { + indexModel.assign(scope.$parent, value); + }; + var indexModel = $parse(iAttributes.rnCarouselIndex); + if (angular.isFunction(indexModel.assign)) { + /* check if this property is assignable then watch it */ + scope.$watch('carouselIndex', function(newValue) { + updateParentIndex(newValue); + }); + scope.$parent.$watch(indexModel, function(newValue, oldValue) { + + if (newValue !== undefined && newValue !== null) { + if (currentSlides && currentSlides.length > 0 && newValue >= currentSlides.length) { + newValue = currentSlides.length - 1; + updateParentIndex(newValue); + } else if (currentSlides && newValue < 0) { + newValue = 0; + updateParentIndex(newValue); + } + if (!locked) { + goToSlide(newValue, { + animate: !init + }); + } + init = false; + } + }); + isIndexBound = true; + + if (options.defaultIndex) { + goToSlide(options.defaultIndex, { + animate: !init + }); + } + } else if (!isNaN(iAttributes.rnCarouselIndex)) { + /* if user just set an initial number, set it */ + goToSlide(parseInt(iAttributes.rnCarouselIndex, 10), { + animate: false + }); + } + } else { + goToSlide(options.defaultIndex, { + animate: !init + }); + init = false; + } + + if (iAttributes.rnCarouselLocked) { + scope.$watch(iAttributes.rnCarouselLocked, function(newValue, oldValue) { + // only bind swipe when it's not switched off + if(newValue === true) { + locked = true; + } else { + locked = false; + } + }); + } + + if (isRepeatBased) { + // use rn-carousel-deep-watch to fight the Angular $watchCollection weakness : https://github.com/angular/angular.js/issues/2621 + // optional because it have some performance impacts (deep watch) + var deepWatch = (iAttributes.rnCarouselDeepWatch!==undefined); + + scope[deepWatch?'$watch':'$watchCollection'](repeatCollection, function(newValue, oldValue) { + //console.log('repeatCollection', currentSlides); + currentSlides = newValue; + // if deepWatch ON ,manually compare objects to guess the new position + if (deepWatch && angular.isArray(newValue)) { + var activeElement = oldValue[scope.carouselIndex]; + var newIndex = getItemIndex(newValue, activeElement, scope.carouselIndex); + goToSlide(newIndex, {animate: false}); + } else { + goToSlide(scope.carouselIndex, {animate: false}); + } + }, true); + } + + function swipeEnd(coords, event, forceAnimation) { + // console.log('swipeEnd', 'scope.carouselIndex', scope.carouselIndex); + // Prevent clicks on buttons inside slider to trigger "swipeEnd" event on touchend/mouseup + // console.log(iAttributes.rnCarouselOnInfiniteScroll); + if (event && !swipeMoved) { + return; + } + unbindMouseUpEvent(); + pressed = false; + swipeMoved = false; + destination = startX - coords.x; + if (destination===0) { + return; + } + if (locked) { + return; + } + offset += (-destination * 100 / elWidth); + if (options.isSequential) { + var minMove = options.moveTreshold * elWidth, + absMove = -destination, + slidesMove = -Math[absMove >= 0 ? 'ceil' : 'floor'](absMove / elWidth), + shouldMove = Math.abs(absMove) > minMove; + + if (currentSlides && (slidesMove + scope.carouselIndex) >= currentSlides.length) { + slidesMove = currentSlides.length - 1 - scope.carouselIndex; + } + if ((slidesMove + scope.carouselIndex) < 0) { + slidesMove = -scope.carouselIndex; + } + var moveOffset = shouldMove ? slidesMove : 0; + + destination = (scope.carouselIndex + moveOffset); + + goToSlide(destination); + if(iAttributes.rnCarouselOnInfiniteScrollRight!==undefined && slidesMove === 0 && scope.carouselIndex !== 0) { + $parse(iAttributes.rnCarouselOnInfiniteScrollRight)(scope) + goToSlide(0); + } + if(iAttributes.rnCarouselOnInfiniteScrollLeft!==undefined && slidesMove === 0 && scope.carouselIndex === 0 && moveOffset === 0) { + $parse(iAttributes.rnCarouselOnInfiniteScrollLeft)(scope) + goToSlide(currentSlides.length); + } + + } else { + scope.$apply(function() { + scope.carouselIndex = parseInt(-offset / 100, 10); + updateBufferIndex(); + }); + + } + + } + + scope.$on('$destroy', function() { + unbindMouseUpEvent(); + }); + + scope.carouselBufferIndex = 0; + scope.carouselBufferSize = options.bufferSize; + + function updateBufferIndex() { + // update and cap te buffer index + var bufferIndex = 0; + var bufferEdgeSize = (scope.carouselBufferSize - 1) / 2; + if (isBuffered) { + if (scope.carouselIndex <= bufferEdgeSize) { + // first buffer part + bufferIndex = 0; + } else if (currentSlides && currentSlides.length < scope.carouselBufferSize) { + // smaller than buffer + bufferIndex = 0; + } else if (currentSlides && scope.carouselIndex > currentSlides.length - scope.carouselBufferSize) { + // last buffer part + bufferIndex = currentSlides.length - scope.carouselBufferSize; + } else { + // compute buffer start + bufferIndex = scope.carouselIndex - bufferEdgeSize; + } + + scope.carouselBufferIndex = bufferIndex; + $timeout(function() { + updateSlidesPosition(offset); + }, 0, false); + } else { + $timeout(function() { + updateSlidesPosition(offset); + }, 0, false); + } + } + + function onOrientationChange() { + updateContainerWidth(); + goToSlide(); + } + + // handle orientation change + var winEl = angular.element($window); + winEl.bind('orientationchange', onOrientationChange); + winEl.bind('resize', onOrientationChange); + + scope.$on('$destroy', function() { + unbindMouseUpEvent(); + winEl.unbind('orientationchange', onOrientationChange); + winEl.unbind('resize', onOrientationChange); + }); + }; + } + }; + } + ]); +})(); + + + +angular.module('angular-carousel.shifty', []) + +.factory('Tweenable', function() { + + /*! shifty - v1.3.4 - 2014-10-29 - http://jeremyckahn.github.io/shifty */ + ;(function (root) { + + /*! + * Shifty Core + * By Jeremy Kahn - jeremyckahn@gmail.com + */ + + var Tweenable = (function () { + + 'use strict'; + + // Aliases that get defined later in this function + var formula; + + // CONSTANTS + var DEFAULT_SCHEDULE_FUNCTION; + var DEFAULT_EASING = 'linear'; + var DEFAULT_DURATION = 500; + var UPDATE_TIME = 1000 / 60; + + var _now = Date.now + ? Date.now + : function () {return +new Date();}; + + var now = typeof SHIFTY_DEBUG_NOW !== 'undefined' ? SHIFTY_DEBUG_NOW : _now; + + if (typeof window !== 'undefined') { + // requestAnimationFrame() shim by Paul Irish (modified for Shifty) + // http://paulirish.com/2011/requestanimationframe-for-smart-animating/ + DEFAULT_SCHEDULE_FUNCTION = window.requestAnimationFrame + || window.webkitRequestAnimationFrame + || window.oRequestAnimationFrame + || window.msRequestAnimationFrame + || (window.mozCancelRequestAnimationFrame + && window.mozRequestAnimationFrame) + || setTimeout; + } else { + DEFAULT_SCHEDULE_FUNCTION = setTimeout; + } + + function noop () { + // NOOP! + } + + /*! + * Handy shortcut for doing a for-in loop. This is not a "normal" each + * function, it is optimized for Shifty. The iterator function only receives + * the property name, not the value. + * @param {Object} obj + * @param {Function(string)} fn + */ + function each (obj, fn) { + var key; + for (key in obj) { + if (Object.hasOwnProperty.call(obj, key)) { + fn(key); + } + } + } + + /*! + * Perform a shallow copy of Object properties. + * @param {Object} targetObject The object to copy into + * @param {Object} srcObject The object to copy from + * @return {Object} A reference to the augmented `targetObj` Object + */ + function shallowCopy (targetObj, srcObj) { + each(srcObj, function (prop) { + targetObj[prop] = srcObj[prop]; + }); + + return targetObj; + } + + /*! + * Copies each property from src onto target, but only if the property to + * copy to target is undefined. + * @param {Object} target Missing properties in this Object are filled in + * @param {Object} src + */ + function defaults (target, src) { + each(src, function (prop) { + if (typeof target[prop] === 'undefined') { + target[prop] = src[prop]; + } + }); + } + + /*! + * Calculates the interpolated tween values of an Object for a given + * timestamp. + * @param {Number} forPosition The position to compute the state for. + * @param {Object} currentState Current state properties. + * @param {Object} originalState: The original state properties the Object is + * tweening from. + * @param {Object} targetState: The destination state properties the Object + * is tweening to. + * @param {number} duration: The length of the tween in milliseconds. + * @param {number} timestamp: The UNIX epoch time at which the tween began. + * @param {Object} easing: This Object's keys must correspond to the keys in + * targetState. + */ + function tweenProps (forPosition, currentState, originalState, targetState, + duration, timestamp, easing) { + var normalizedPosition = (forPosition - timestamp) / duration; + + var prop; + for (prop in currentState) { + if (currentState.hasOwnProperty(prop)) { + currentState[prop] = tweenProp(originalState[prop], + targetState[prop], formula[easing[prop]], normalizedPosition); + } + } + + return currentState; + } + + /*! + * Tweens a single property. + * @param {number} start The value that the tween started from. + * @param {number} end The value that the tween should end at. + * @param {Function} easingFunc The easing curve to apply to the tween. + * @param {number} position The normalized position (between 0.0 and 1.0) to + * calculate the midpoint of 'start' and 'end' against. + * @return {number} The tweened value. + */ + function tweenProp (start, end, easingFunc, position) { + return start + (end - start) * easingFunc(position); + } + + /*! + * Applies a filter to Tweenable instance. + * @param {Tweenable} tweenable The `Tweenable` instance to call the filter + * upon. + * @param {String} filterName The name of the filter to apply. + */ + function applyFilter (tweenable, filterName) { + var filters = Tweenable.prototype.filter; + var args = tweenable._filterArgs; + + each(filters, function (name) { + if (typeof filters[name][filterName] !== 'undefined') { + filters[name][filterName].apply(tweenable, args); + } + }); + } + + var timeoutHandler_endTime; + var timeoutHandler_currentTime; + var timeoutHandler_isEnded; + var timeoutHandler_offset; + /*! + * Handles the update logic for one step of a tween. + * @param {Tweenable} tweenable + * @param {number} timestamp + * @param {number} duration + * @param {Object} currentState + * @param {Object} originalState + * @param {Object} targetState + * @param {Object} easing + * @param {Function(Object, *, number)} step + * @param {Function(Function,number)}} schedule + */ + function timeoutHandler (tweenable, timestamp, duration, currentState, + originalState, targetState, easing, step, schedule) { + timeoutHandler_endTime = timestamp + duration; + timeoutHandler_currentTime = Math.min(now(), timeoutHandler_endTime); + timeoutHandler_isEnded = + timeoutHandler_currentTime >= timeoutHandler_endTime; + + timeoutHandler_offset = duration - ( + timeoutHandler_endTime - timeoutHandler_currentTime); + + if (tweenable.isPlaying() && !timeoutHandler_isEnded) { + tweenable._scheduleId = schedule(tweenable._timeoutHandler, UPDATE_TIME); + + applyFilter(tweenable, 'beforeTween'); + tweenProps(timeoutHandler_currentTime, currentState, originalState, + targetState, duration, timestamp, easing); + applyFilter(tweenable, 'afterTween'); + + step(currentState, tweenable._attachment, timeoutHandler_offset); + } else if (timeoutHandler_isEnded) { + step(targetState, tweenable._attachment, timeoutHandler_offset); + tweenable.stop(true); + } + } + + + /*! + * Creates a usable easing Object from either a string or another easing + * Object. If `easing` is an Object, then this function clones it and fills + * in the missing properties with "linear". + * @param {Object} fromTweenParams + * @param {Object|string} easing + */ + function composeEasingObject (fromTweenParams, easing) { + var composedEasing = {}; + + if (typeof easing === 'string') { + each(fromTweenParams, function (prop) { + composedEasing[prop] = easing; + }); + } else { + each(fromTweenParams, function (prop) { + if (!composedEasing[prop]) { + composedEasing[prop] = easing[prop] || DEFAULT_EASING; + } + }); + } + + return composedEasing; + } + + /** + * Tweenable constructor. + * @param {Object=} opt_initialState The values that the initial tween should start at if a "from" object is not provided to Tweenable#tween. + * @param {Object=} opt_config See Tweenable.prototype.setConfig() + * @constructor + */ + function Tweenable (opt_initialState, opt_config) { + this._currentState = opt_initialState || {}; + this._configured = false; + this._scheduleFunction = DEFAULT_SCHEDULE_FUNCTION; + + // To prevent unnecessary calls to setConfig do not set default configuration here. + // Only set default configuration immediately before tweening if none has been set. + if (typeof opt_config !== 'undefined') { + this.setConfig(opt_config); + } + } + + /** + * Configure and start a tween. + * @param {Object=} opt_config See Tweenable.prototype.setConfig() + * @return {Tweenable} + */ + Tweenable.prototype.tween = function (opt_config) { + if (this._isTweening) { + return this; + } + + // Only set default config if no configuration has been set previously and none is provided now. + if (opt_config !== undefined || !this._configured) { + this.setConfig(opt_config); + } + + this._timestamp = now(); + this._start(this.get(), this._attachment); + return this.resume(); + }; + + /** + * Sets the tween configuration. `config` may have the following options: + * + * - __from__ (_Object=_): Starting position. If omitted, the current state is used. + * - __to__ (_Object=_): Ending position. + * - __duration__ (_number=_): How many milliseconds to animate for. + * - __start__ (_Function(Object)_): Function to execute when the tween begins. Receives the state of the tween as the first parameter. Attachment is the second parameter. + * - __step__ (_Function(Object, *, number)_): Function to execute on every tick. Receives the state of the tween as the first parameter. Attachment is the second parameter, and the time elapsed since the start of the tween is the third parameter. This function is not called on the final step of the animation, but `finish` is. + * - __finish__ (_Function(Object, *)_): Function to execute upon tween completion. Receives the state of the tween as the first parameter. Attachment is the second parameter. + * - __easing__ (_Object|string=_): Easing curve name(s) to use for the tween. + * - __attachment__ (_Object|string|any=_): Value that is attached to this instance and passed on to the step/start/finish methods. + * @param {Object} config + * @return {Tweenable} + */ + Tweenable.prototype.setConfig = function (config) { + config = config || {}; + this._configured = true; + + // Attach something to this Tweenable instance (e.g.: a DOM element, an object, a string, etc.); + this._attachment = config.attachment; + + // Init the internal state + this._pausedAtTime = null; + this._scheduleId = null; + this._start = config.start || noop; + this._step = config.step || noop; + this._finish = config.finish || noop; + this._duration = config.duration || DEFAULT_DURATION; + this._currentState = config.from || this.get(); + this._originalState = this.get(); + this._targetState = config.to || this.get(); + + // Aliases used below + var currentState = this._currentState; + var targetState = this._targetState; + + // Ensure that there is always something to tween to. + defaults(targetState, currentState); + + this._easing = composeEasingObject( + currentState, config.easing || DEFAULT_EASING); + + this._filterArgs = + [currentState, this._originalState, targetState, this._easing]; + + applyFilter(this, 'tweenCreated'); + return this; + }; + + /** + * Gets the current state. + * @return {Object} + */ + Tweenable.prototype.get = function () { + return shallowCopy({}, this._currentState); + }; + + /** + * Sets the current state. + * @param {Object} state + */ + Tweenable.prototype.set = function (state) { + this._currentState = state; + }; + + /** + * Pauses a tween. Paused tweens can be resumed from the point at which they were paused. This is different than [`stop()`](#stop), as that method causes a tween to start over when it is resumed. + * @return {Tweenable} + */ + Tweenable.prototype.pause = function () { + this._pausedAtTime = now(); + this._isPaused = true; + return this; + }; + + /** + * Resumes a paused tween. + * @return {Tweenable} + */ + Tweenable.prototype.resume = function () { + if (this._isPaused) { + this._timestamp += now() - this._pausedAtTime; + } + + this._isPaused = false; + this._isTweening = true; + + var self = this; + this._timeoutHandler = function () { + timeoutHandler(self, self._timestamp, self._duration, self._currentState, + self._originalState, self._targetState, self._easing, self._step, + self._scheduleFunction); + }; + + this._timeoutHandler(); + + return this; + }; + + /** + * Move the state of the animation to a specific point in the tween's timeline. + * If the animation is not running, this will cause the `step` handlers to be + * called. + * @param {millisecond} millisecond The millisecond of the animation to seek to. + * @return {Tweenable} + */ + Tweenable.prototype.seek = function (millisecond) { + this._timestamp = now() - millisecond; + + if (!this.isPlaying()) { + this._isTweening = true; + this._isPaused = false; + + // If the animation is not running, call timeoutHandler to make sure that + // any step handlers are run. + timeoutHandler(this, this._timestamp, this._duration, this._currentState, + this._originalState, this._targetState, this._easing, this._step, + this._scheduleFunction); + + this._timeoutHandler(); + this.pause(); + } + + return this; + }; + + /** + * Stops and cancels a tween. + * @param {boolean=} gotoEnd If false or omitted, the tween just stops at its current state, and the "finish" handler is not invoked. If true, the tweened object's values are instantly set to the target values, and "finish" is invoked. + * @return {Tweenable} + */ + Tweenable.prototype.stop = function (gotoEnd) { + this._isTweening = false; + this._isPaused = false; + this._timeoutHandler = noop; + + (root.cancelAnimationFrame || + root.webkitCancelAnimationFrame || + root.oCancelAnimationFrame || + root.msCancelAnimationFrame || + root.mozCancelRequestAnimationFrame || + root.clearTimeout)(this._scheduleId); + + if (gotoEnd) { + shallowCopy(this._currentState, this._targetState); + applyFilter(this, 'afterTweenEnd'); + this._finish.call(this, this._currentState, this._attachment); + } + + return this; + }; + + /** + * Returns whether or not a tween is running. + * @return {boolean} + */ + Tweenable.prototype.isPlaying = function () { + return this._isTweening && !this._isPaused; + }; + + /** + * Sets a custom schedule function. + * + * If a custom function is not set the default one is used [`requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame) if available, otherwise [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/Window.setTimeout)). + * + * @param {Function(Function,number)} scheduleFunction The function to be called to schedule the next frame to be rendered + */ + Tweenable.prototype.setScheduleFunction = function (scheduleFunction) { + this._scheduleFunction = scheduleFunction; + }; + + /** + * `delete`s all "own" properties. Call this when the `Tweenable` instance is no longer needed to free memory. + */ + Tweenable.prototype.dispose = function () { + var prop; + for (prop in this) { + if (this.hasOwnProperty(prop)) { + delete this[prop]; + } + } + }; + + /*! + * Filters are used for transforming the properties of a tween at various + * points in a Tweenable's life cycle. See the README for more info on this. + */ + Tweenable.prototype.filter = {}; + + /*! + * This object contains all of the tweens available to Shifty. It is extendible - simply attach properties to the Tweenable.prototype.formula Object following the same format at linear. + * + * `pos` should be a normalized `number` (between 0 and 1). + */ + Tweenable.prototype.formula = { + linear: function (pos) { + return pos; + } + }; + + formula = Tweenable.prototype.formula; + + shallowCopy(Tweenable, { + 'now': now + ,'each': each + ,'tweenProps': tweenProps + ,'tweenProp': tweenProp + ,'applyFilter': applyFilter + ,'shallowCopy': shallowCopy + ,'defaults': defaults + ,'composeEasingObject': composeEasingObject + }); + + root.Tweenable = Tweenable; + return Tweenable; + + } ()); + + /*! + * All equations are adapted from Thomas Fuchs' [Scripty2](https://github.com/madrobby/scripty2/blob/master/src/effects/transitions/penner.js). + * + * Based on Easing Equations (c) 2003 [Robert Penner](http://www.robertpenner.com/), all rights reserved. This work is [subject to terms](http://www.robertpenner.com/easing_terms_of_use.html). + */ + + /*! + * TERMS OF USE - EASING EQUATIONS + * Open source under the BSD License. + * Easing Equations (c) 2003 Robert Penner, all rights reserved. + */ + + ;(function () { + + Tweenable.shallowCopy(Tweenable.prototype.formula, { + easeInQuad: function (pos) { + return Math.pow(pos, 2); + }, + + easeOutQuad: function (pos) { + return -(Math.pow((pos - 1), 2) - 1); + }, + + easeInOutQuad: function (pos) { + if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,2);} + return -0.5 * ((pos -= 2) * pos - 2); + }, + + easeInCubic: function (pos) { + return Math.pow(pos, 3); + }, + + easeOutCubic: function (pos) { + return (Math.pow((pos - 1), 3) + 1); + }, + + easeInOutCubic: function (pos) { + if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,3);} + return 0.5 * (Math.pow((pos - 2),3) + 2); + }, + + easeInQuart: function (pos) { + return Math.pow(pos, 4); + }, + + easeOutQuart: function (pos) { + return -(Math.pow((pos - 1), 4) - 1); + }, + + easeInOutQuart: function (pos) { + if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,4);} + return -0.5 * ((pos -= 2) * Math.pow(pos,3) - 2); + }, + + easeInQuint: function (pos) { + return Math.pow(pos, 5); + }, + + easeOutQuint: function (pos) { + return (Math.pow((pos - 1), 5) + 1); + }, + + easeInOutQuint: function (pos) { + if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,5);} + return 0.5 * (Math.pow((pos - 2),5) + 2); + }, + + easeInSine: function (pos) { + return -Math.cos(pos * (Math.PI / 2)) + 1; + }, + + easeOutSine: function (pos) { + return Math.sin(pos * (Math.PI / 2)); + }, + + easeInOutSine: function (pos) { + return (-0.5 * (Math.cos(Math.PI * pos) - 1)); + }, + + easeInExpo: function (pos) { + return (pos === 0) ? 0 : Math.pow(2, 10 * (pos - 1)); + }, + + easeOutExpo: function (pos) { + return (pos === 1) ? 1 : -Math.pow(2, -10 * pos) + 1; + }, + + easeInOutExpo: function (pos) { + if (pos === 0) {return 0;} + if (pos === 1) {return 1;} + if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(2,10 * (pos - 1));} + return 0.5 * (-Math.pow(2, -10 * --pos) + 2); + }, + + easeInCirc: function (pos) { + return -(Math.sqrt(1 - (pos * pos)) - 1); + }, + + easeOutCirc: function (pos) { + return Math.sqrt(1 - Math.pow((pos - 1), 2)); + }, + + easeInOutCirc: function (pos) { + if ((pos /= 0.5) < 1) {return -0.5 * (Math.sqrt(1 - pos * pos) - 1);} + return 0.5 * (Math.sqrt(1 - (pos -= 2) * pos) + 1); + }, + + easeOutBounce: function (pos) { + if ((pos) < (1 / 2.75)) { + return (7.5625 * pos * pos); + } else if (pos < (2 / 2.75)) { + return (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75); + } else if (pos < (2.5 / 2.75)) { + return (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375); + } else { + return (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375); + } + }, + + easeInBack: function (pos) { + var s = 1.70158; + return (pos) * pos * ((s + 1) * pos - s); + }, + + easeOutBack: function (pos) { + var s = 1.70158; + return (pos = pos - 1) * pos * ((s + 1) * pos + s) + 1; + }, + + easeInOutBack: function (pos) { + var s = 1.70158; + if ((pos /= 0.5) < 1) {return 0.5 * (pos * pos * (((s *= (1.525)) + 1) * pos - s));} + return 0.5 * ((pos -= 2) * pos * (((s *= (1.525)) + 1) * pos + s) + 2); + }, + + elastic: function (pos) { + return -1 * Math.pow(4,-8 * pos) * Math.sin((pos * 6 - 1) * (2 * Math.PI) / 2) + 1; + }, + + swingFromTo: function (pos) { + var s = 1.70158; + return ((pos /= 0.5) < 1) ? 0.5 * (pos * pos * (((s *= (1.525)) + 1) * pos - s)) : + 0.5 * ((pos -= 2) * pos * (((s *= (1.525)) + 1) * pos + s) + 2); + }, + + swingFrom: function (pos) { + var s = 1.70158; + return pos * pos * ((s + 1) * pos - s); + }, + + swingTo: function (pos) { + var s = 1.70158; + return (pos -= 1) * pos * ((s + 1) * pos + s) + 1; + }, + + bounce: function (pos) { + if (pos < (1 / 2.75)) { + return (7.5625 * pos * pos); + } else if (pos < (2 / 2.75)) { + return (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75); + } else if (pos < (2.5 / 2.75)) { + return (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375); + } else { + return (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375); + } + }, + + bouncePast: function (pos) { + if (pos < (1 / 2.75)) { + return (7.5625 * pos * pos); + } else if (pos < (2 / 2.75)) { + return 2 - (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75); + } else if (pos < (2.5 / 2.75)) { + return 2 - (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375); + } else { + return 2 - (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375); + } + }, + + easeFromTo: function (pos) { + if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,4);} + return -0.5 * ((pos -= 2) * Math.pow(pos,3) - 2); + }, + + easeFrom: function (pos) { + return Math.pow(pos,4); + }, + + easeTo: function (pos) { + return Math.pow(pos,0.25); + } + }); + + }()); + + /*! + * The Bezier magic in this file is adapted/copied almost wholesale from + * [Scripty2](https://github.com/madrobby/scripty2/blob/master/src/effects/transitions/cubic-bezier.js), + * which was adapted from Apple code (which probably came from + * [here](http://opensource.apple.com/source/WebCore/WebCore-955.66/platform/graphics/UnitBezier.h)). + * Special thanks to Apple and Thomas Fuchs for much of this code. + */ + + /*! + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder(s) nor the names of any + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + ;(function () { + // port of webkit cubic bezier handling by http://www.netzgesta.de/dev/ + function cubicBezierAtTime(t,p1x,p1y,p2x,p2y,duration) { + var ax = 0,bx = 0,cx = 0,ay = 0,by = 0,cy = 0; + function sampleCurveX(t) {return ((ax * t + bx) * t + cx) * t;} + function sampleCurveY(t) {return ((ay * t + by) * t + cy) * t;} + function sampleCurveDerivativeX(t) {return (3.0 * ax * t + 2.0 * bx) * t + cx;} + function solveEpsilon(duration) {return 1.0 / (200.0 * duration);} + function solve(x,epsilon) {return sampleCurveY(solveCurveX(x,epsilon));} + function fabs(n) {if (n >= 0) {return n;}else {return 0 - n;}} + function solveCurveX(x,epsilon) { + var t0,t1,t2,x2,d2,i; + for (t2 = x, i = 0; i < 8; i++) {x2 = sampleCurveX(t2) - x; if (fabs(x2) < epsilon) {return t2;} d2 = sampleCurveDerivativeX(t2); if (fabs(d2) < 1e-6) {break;} t2 = t2 - x2 / d2;} + t0 = 0.0; t1 = 1.0; t2 = x; if (t2 < t0) {return t0;} if (t2 > t1) {return t1;} + while (t0 < t1) {x2 = sampleCurveX(t2); if (fabs(x2 - x) < epsilon) {return t2;} if (x > x2) {t0 = t2;}else {t1 = t2;} t2 = (t1 - t0) * 0.5 + t0;} + return t2; // Failure. + } + cx = 3.0 * p1x; bx = 3.0 * (p2x - p1x) - cx; ax = 1.0 - cx - bx; cy = 3.0 * p1y; by = 3.0 * (p2y - p1y) - cy; ay = 1.0 - cy - by; + return solve(t, solveEpsilon(duration)); + } + /*! + * getCubicBezierTransition(x1, y1, x2, y2) -> Function + * + * Generates a transition easing function that is compatible + * with WebKit's CSS transitions `-webkit-transition-timing-function` + * CSS property. + * + * The W3C has more information about + * <a href="http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag"> + * CSS3 transition timing functions</a>. + * + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @return {function} + */ + function getCubicBezierTransition (x1, y1, x2, y2) { + return function (pos) { + return cubicBezierAtTime(pos,x1,y1,x2,y2,1); + }; + } + // End ported code + + /** + * Creates a Bezier easing function and attaches it to `Tweenable.prototype.formula`. This function gives you total control over the easing curve. Matthew Lein's [Ceaser](http://matthewlein.com/ceaser/) is a useful tool for visualizing the curves you can make with this function. + * + * @param {string} name The name of the easing curve. Overwrites the old easing function on Tweenable.prototype.formula if it exists. + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @return {function} The easing function that was attached to Tweenable.prototype.formula. + */ + Tweenable.setBezierFunction = function (name, x1, y1, x2, y2) { + var cubicBezierTransition = getCubicBezierTransition(x1, y1, x2, y2); + cubicBezierTransition.x1 = x1; + cubicBezierTransition.y1 = y1; + cubicBezierTransition.x2 = x2; + cubicBezierTransition.y2 = y2; + + return Tweenable.prototype.formula[name] = cubicBezierTransition; + }; + + + /** + * `delete`s an easing function from `Tweenable.prototype.formula`. Be careful with this method, as it `delete`s whatever easing formula matches `name` (which means you can delete default Shifty easing functions). + * + * @param {string} name The name of the easing function to delete. + * @return {function} + */ + Tweenable.unsetBezierFunction = function (name) { + delete Tweenable.prototype.formula[name]; + }; + + })(); + + ;(function () { + + function getInterpolatedValues ( + from, current, targetState, position, easing) { + return Tweenable.tweenProps( + position, current, from, targetState, 1, 0, easing); + } + + // Fake a Tweenable and patch some internals. This approach allows us to + // skip uneccessary processing and object recreation, cutting down on garbage + // collection pauses. + var mockTweenable = new Tweenable(); + mockTweenable._filterArgs = []; + + /** + * Compute the midpoint of two Objects. This method effectively calculates a specific frame of animation that [Tweenable#tween](shifty.core.js.html#tween) does many times over the course of a tween. + * + * Example: + * + * var interpolatedValues = Tweenable.interpolate({ + * width: '100px', + * opacity: 0, + * color: '#fff' + * }, { + * width: '200px', + * opacity: 1, + * color: '#000' + * }, 0.5); + * + * console.log(interpolatedValues); + * // {opacity: 0.5, width: "150px", color: "rgb(127,127,127)"} + * + * @param {Object} from The starting values to tween from. + * @param {Object} targetState The ending values to tween to. + * @param {number} position The normalized position value (between 0.0 and 1.0) to interpolate the values between `from` and `to` for. `from` represents 0 and `to` represents `1`. + * @param {string|Object} easing The easing curve(s) to calculate the midpoint against. You can reference any easing function attached to `Tweenable.prototype.formula`. If omitted, this defaults to "linear". + * @return {Object} + */ + Tweenable.interpolate = function (from, targetState, position, easing) { + var current = Tweenable.shallowCopy({}, from); + var easingObject = Tweenable.composeEasingObject( + from, easing || 'linear'); + + mockTweenable.set({}); + + // Alias and reuse the _filterArgs array instead of recreating it. + var filterArgs = mockTweenable._filterArgs; + filterArgs.length = 0; + filterArgs[0] = current; + filterArgs[1] = from; + filterArgs[2] = targetState; + filterArgs[3] = easingObject; + + // Any defined value transformation must be applied + Tweenable.applyFilter(mockTweenable, 'tweenCreated'); + Tweenable.applyFilter(mockTweenable, 'beforeTween'); + + var interpolatedValues = getInterpolatedValues( + from, current, targetState, position, easingObject); + + // Transform values back into their original format + Tweenable.applyFilter(mockTweenable, 'afterTween'); + + return interpolatedValues; + }; + + }()); + + /** + * Adds string interpolation support to Shifty. + * + * The Token extension allows Shifty to tween numbers inside of strings. Among + * other things, this allows you to animate CSS properties. For example, you + * can do this: + * + * var tweenable = new Tweenable(); + * tweenable.tween({ + * from: { transform: 'translateX(45px)'}, + * to: { transform: 'translateX(90xp)'} + * }); + * + * ` ` + * `translateX(45)` will be tweened to `translateX(90)`. To demonstrate: + * + * var tweenable = new Tweenable(); + * tweenable.tween({ + * from: { transform: 'translateX(45px)'}, + * to: { transform: 'translateX(90px)'}, + * step: function (state) { + * console.log(state.transform); + * } + * }); + * + * ` ` + * The above snippet will log something like this in the console: + * + * translateX(60.3px) + * ... + * translateX(76.05px) + * ... + * translateX(90px) + * + * ` ` + * Another use for this is animating colors: + * + * var tweenable = new Tweenable(); + * tweenable.tween({ + * from: { color: 'rgb(0,255,0)'}, + * to: { color: 'rgb(255,0,255)'}, + * step: function (state) { + * console.log(state.color); + * } + * }); + * + * ` ` + * The above snippet will log something like this: + * + * rgb(84,170,84) + * ... + * rgb(170,84,170) + * ... + * rgb(255,0,255) + * + * ` ` + * This extension also supports hexadecimal colors, in both long (`#ff00ff`) + * and short (`#f0f`) forms. Be aware that hexadecimal input values will be + * converted into the equivalent RGB output values. This is done to optimize + * for performance. + * + * var tweenable = new Tweenable(); + * tweenable.tween({ + * from: { color: '#0f0'}, + * to: { color: '#f0f'}, + * step: function (state) { + * console.log(state.color); + * } + * }); + * + * ` ` + * This snippet will generate the same output as the one before it because + * equivalent values were supplied (just in hexadecimal form rather than RGB): + * + * rgb(84,170,84) + * ... + * rgb(170,84,170) + * ... + * rgb(255,0,255) + * + * ` ` + * ` ` + * ## Easing support + * + * Easing works somewhat differently in the Token extension. This is because + * some CSS properties have multiple values in them, and you might need to + * tween each value along its own easing curve. A basic example: + * + * var tweenable = new Tweenable(); + * tweenable.tween({ + * from: { transform: 'translateX(0px) translateY(0px)'}, + * to: { transform: 'translateX(100px) translateY(100px)'}, + * easing: { transform: 'easeInQuad' }, + * step: function (state) { + * console.log(state.transform); + * } + * }); + * + * ` ` + * The above snippet create values like this: + * + * translateX(11.560000000000002px) translateY(11.560000000000002px) + * ... + * translateX(46.24000000000001px) translateY(46.24000000000001px) + * ... + * translateX(100px) translateY(100px) + * + * ` ` + * In this case, the values for `translateX` and `translateY` are always the + * same for each step of the tween, because they have the same start and end + * points and both use the same easing curve. We can also tween `translateX` + * and `translateY` along independent curves: + * + * var tweenable = new Tweenable(); + * tweenable.tween({ + * from: { transform: 'translateX(0px) translateY(0px)'}, + * to: { transform: 'translateX(100px) translateY(100px)'}, + * easing: { transform: 'easeInQuad bounce' }, + * step: function (state) { + * console.log(state.transform); + * } + * }); + * + * ` ` + * The above snippet create values like this: + * + * translateX(10.89px) translateY(82.355625px) + * ... + * translateX(44.89000000000001px) translateY(86.73062500000002px) + * ... + * translateX(100px) translateY(100px) + * + * ` ` + * `translateX` and `translateY` are not in sync anymore, because `easeInQuad` + * was specified for `translateX` and `bounce` for `translateY`. Mixing and + * matching easing curves can make for some interesting motion in your + * animations. + * + * The order of the space-separated easing curves correspond the token values + * they apply to. If there are more token values than easing curves listed, + * the last easing curve listed is used. + */ + function token () { + // Functionality for this extension runs implicitly if it is loaded. + } /*!*/ + + // token function is defined above only so that dox-foundation sees it as + // documentation and renders it. It is never used, and is optimized away at + // build time. + + ;(function (Tweenable) { + + /*! + * @typedef {{ + * formatString: string + * chunkNames: Array.<string> + * }} + */ + var formatManifest; + + // CONSTANTS + + var R_NUMBER_COMPONENT = /(\d|\-|\.)/; + var R_FORMAT_CHUNKS = /([^\-0-9\.]+)/g; + var R_UNFORMATTED_VALUES = /[0-9.\-]+/g; + var R_RGB = new RegExp( + 'rgb\\(' + R_UNFORMATTED_VALUES.source + + (/,\s*/.source) + R_UNFORMATTED_VALUES.source + + (/,\s*/.source) + R_UNFORMATTED_VALUES.source + '\\)', 'g'); + var R_RGB_PREFIX = /^.*\(/; + var R_HEX = /#([0-9]|[a-f]){3,6}/gi; + var VALUE_PLACEHOLDER = 'VAL'; + + // HELPERS + + var getFormatChunksFrom_accumulator = []; + /*! + * @param {Array.number} rawValues + * @param {string} prefix + * + * @return {Array.<string>} + */ + function getFormatChunksFrom (rawValues, prefix) { + getFormatChunksFrom_accumulator.length = 0; + + var rawValuesLength = rawValues.length; + var i; + + for (i = 0; i < rawValuesLength; i++) { + getFormatChunksFrom_accumulator.push('_' + prefix + '_' + i); + } + + return getFormatChunksFrom_accumulator; + } + + /*! + * @param {string} formattedString + * + * @return {string} + */ + function getFormatStringFrom (formattedString) { + var chunks = formattedString.match(R_FORMAT_CHUNKS); + + if (!chunks) { + // chunks will be null if there were no tokens to parse in + // formattedString (for example, if formattedString is '2'). Coerce + // chunks to be useful here. + chunks = ['', '']; + + // If there is only one chunk, assume that the string is a number + // followed by a token... + // NOTE: This may be an unwise assumption. + } else if (chunks.length === 1 || + // ...or if the string starts with a number component (".", "-", or a + // digit)... + formattedString[0].match(R_NUMBER_COMPONENT)) { + // ...prepend an empty string here to make sure that the formatted number + // is properly replaced by VALUE_PLACEHOLDER + chunks.unshift(''); + } + + return chunks.join(VALUE_PLACEHOLDER); + } + + /*! + * Convert all hex color values within a string to an rgb string. + * + * @param {Object} stateObject + * + * @return {Object} The modified obj + */ + function sanitizeObjectForHexProps (stateObject) { + Tweenable.each(stateObject, function (prop) { + var currentProp = stateObject[prop]; + + if (typeof currentProp === 'string' && currentProp.match(R_HEX)) { + stateObject[prop] = sanitizeHexChunksToRGB(currentProp); + } + }); + } + + /*! + * @param {string} str + * + * @return {string} + */ + function sanitizeHexChunksToRGB (str) { + return filterStringChunks(R_HEX, str, convertHexToRGB); + } + + /*! + * @param {string} hexString + * + * @return {string} + */ + function convertHexToRGB (hexString) { + var rgbArr = hexToRGBArray(hexString); + return 'rgb(' + rgbArr[0] + ',' + rgbArr[1] + ',' + rgbArr[2] + ')'; + } + + var hexToRGBArray_returnArray = []; + /*! + * Convert a hexadecimal string to an array with three items, one each for + * the red, blue, and green decimal values. + * + * @param {string} hex A hexadecimal string. + * + * @returns {Array.<number>} The converted Array of RGB values if `hex` is a + * valid string, or an Array of three 0's. + */ + function hexToRGBArray (hex) { + + hex = hex.replace(/#/, ''); + + // If the string is a shorthand three digit hex notation, normalize it to + // the standard six digit notation + if (hex.length === 3) { + hex = hex.split(''); + hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; + } + + hexToRGBArray_returnArray[0] = hexToDec(hex.substr(0, 2)); + hexToRGBArray_returnArray[1] = hexToDec(hex.substr(2, 2)); + hexToRGBArray_returnArray[2] = hexToDec(hex.substr(4, 2)); + + return hexToRGBArray_returnArray; + } + + /*! + * Convert a base-16 number to base-10. + * + * @param {Number|String} hex The value to convert + * + * @returns {Number} The base-10 equivalent of `hex`. + */ + function hexToDec (hex) { + return parseInt(hex, 16); + } + + /*! + * Runs a filter operation on all chunks of a string that match a RegExp + * + * @param {RegExp} pattern + * @param {string} unfilteredString + * @param {function(string)} filter + * + * @return {string} + */ + function filterStringChunks (pattern, unfilteredString, filter) { + var pattenMatches = unfilteredString.match(pattern); + var filteredString = unfilteredString.replace(pattern, VALUE_PLACEHOLDER); + + if (pattenMatches) { + var pattenMatchesLength = pattenMatches.length; + var currentChunk; + + for (var i = 0; i < pattenMatchesLength; i++) { + currentChunk = pattenMatches.shift(); + filteredString = filteredString.replace( + VALUE_PLACEHOLDER, filter(currentChunk)); + } + } + + return filteredString; + } + + /*! + * Check for floating point values within rgb strings and rounds them. + * + * @param {string} formattedString + * + * @return {string} + */ + function sanitizeRGBChunks (formattedString) { + return filterStringChunks(R_RGB, formattedString, sanitizeRGBChunk); + } + + /*! + * @param {string} rgbChunk + * + * @return {string} + */ + function sanitizeRGBChunk (rgbChunk) { + var numbers = rgbChunk.match(R_UNFORMATTED_VALUES); + var numbersLength = numbers.length; + var sanitizedString = rgbChunk.match(R_RGB_PREFIX)[0]; + + for (var i = 0; i < numbersLength; i++) { + sanitizedString += parseInt(numbers[i], 10) + ','; + } + + sanitizedString = sanitizedString.slice(0, -1) + ')'; + + return sanitizedString; + } + + /*! + * @param {Object} stateObject + * + * @return {Object} An Object of formatManifests that correspond to + * the string properties of stateObject + */ + function getFormatManifests (stateObject) { + var manifestAccumulator = {}; + + Tweenable.each(stateObject, function (prop) { + var currentProp = stateObject[prop]; + + if (typeof currentProp === 'string') { + var rawValues = getValuesFrom(currentProp); + + manifestAccumulator[prop] = { + 'formatString': getFormatStringFrom(currentProp) + ,'chunkNames': getFormatChunksFrom(rawValues, prop) + }; + } + }); + + return manifestAccumulator; + } + + /*! + * @param {Object} stateObject + * @param {Object} formatManifests + */ + function expandFormattedProperties (stateObject, formatManifests) { + Tweenable.each(formatManifests, function (prop) { + var currentProp = stateObject[prop]; + var rawValues = getValuesFrom(currentProp); + var rawValuesLength = rawValues.length; + + for (var i = 0; i < rawValuesLength; i++) { + stateObject[formatManifests[prop].chunkNames[i]] = +rawValues[i]; + } + + delete stateObject[prop]; + }); + } + + /*! + * @param {Object} stateObject + * @param {Object} formatManifests + */ + function collapseFormattedProperties (stateObject, formatManifests) { + Tweenable.each(formatManifests, function (prop) { + var currentProp = stateObject[prop]; + var formatChunks = extractPropertyChunks( + stateObject, formatManifests[prop].chunkNames); + var valuesList = getValuesList( + formatChunks, formatManifests[prop].chunkNames); + currentProp = getFormattedValues( + formatManifests[prop].formatString, valuesList); + stateObject[prop] = sanitizeRGBChunks(currentProp); + }); + } + + /*! + * @param {Object} stateObject + * @param {Array.<string>} chunkNames + * + * @return {Object} The extracted value chunks. + */ + function extractPropertyChunks (stateObject, chunkNames) { + var extractedValues = {}; + var currentChunkName, chunkNamesLength = chunkNames.length; + + for (var i = 0; i < chunkNamesLength; i++) { + currentChunkName = chunkNames[i]; + extractedValues[currentChunkName] = stateObject[currentChunkName]; + delete stateObject[currentChunkName]; + } + + return extractedValues; + } + + var getValuesList_accumulator = []; + /*! + * @param {Object} stateObject + * @param {Array.<string>} chunkNames + * + * @return {Array.<number>} + */ + function getValuesList (stateObject, chunkNames) { + getValuesList_accumulator.length = 0; + var chunkNamesLength = chunkNames.length; + + for (var i = 0; i < chunkNamesLength; i++) { + getValuesList_accumulator.push(stateObject[chunkNames[i]]); + } + + return getValuesList_accumulator; + } + + /*! + * @param {string} formatString + * @param {Array.<number>} rawValues + * + * @return {string} + */ + function getFormattedValues (formatString, rawValues) { + var formattedValueString = formatString; + var rawValuesLength = rawValues.length; + + for (var i = 0; i < rawValuesLength; i++) { + formattedValueString = formattedValueString.replace( + VALUE_PLACEHOLDER, +rawValues[i].toFixed(4)); + } + + return formattedValueString; + } + + /*! + * Note: It's the duty of the caller to convert the Array elements of the + * return value into numbers. This is a performance optimization. + * + * @param {string} formattedString + * + * @return {Array.<string>|null} + */ + function getValuesFrom (formattedString) { + return formattedString.match(R_UNFORMATTED_VALUES); + } + + /*! + * @param {Object} easingObject + * @param {Object} tokenData + */ + function expandEasingObject (easingObject, tokenData) { + Tweenable.each(tokenData, function (prop) { + var currentProp = tokenData[prop]; + var chunkNames = currentProp.chunkNames; + var chunkLength = chunkNames.length; + var easingChunks = easingObject[prop].split(' '); + var lastEasingChunk = easingChunks[easingChunks.length - 1]; + + for (var i = 0; i < chunkLength; i++) { + easingObject[chunkNames[i]] = easingChunks[i] || lastEasingChunk; + } + + delete easingObject[prop]; + }); + } + + /*! + * @param {Object} easingObject + * @param {Object} tokenData + */ + function collapseEasingObject (easingObject, tokenData) { + Tweenable.each(tokenData, function (prop) { + var currentProp = tokenData[prop]; + var chunkNames = currentProp.chunkNames; + var chunkLength = chunkNames.length; + var composedEasingString = ''; + + for (var i = 0; i < chunkLength; i++) { + composedEasingString += ' ' + easingObject[chunkNames[i]]; + delete easingObject[chunkNames[i]]; + } + + easingObject[prop] = composedEasingString.substr(1); + }); + } + + Tweenable.prototype.filter.token = { + 'tweenCreated': function (currentState, fromState, toState, easingObject) { + sanitizeObjectForHexProps(currentState); + sanitizeObjectForHexProps(fromState); + sanitizeObjectForHexProps(toState); + this._tokenData = getFormatManifests(currentState); + }, + + 'beforeTween': function (currentState, fromState, toState, easingObject) { + expandEasingObject(easingObject, this._tokenData); + expandFormattedProperties(currentState, this._tokenData); + expandFormattedProperties(fromState, this._tokenData); + expandFormattedProperties(toState, this._tokenData); + }, + + 'afterTween': function (currentState, fromState, toState, easingObject) { + collapseFormattedProperties(currentState, this._tokenData); + collapseFormattedProperties(fromState, this._tokenData); + collapseFormattedProperties(toState, this._tokenData); + collapseEasingObject(easingObject, this._tokenData); + } + }; + + } (Tweenable)); + + }(window)); + + return window.Tweenable; +}); + +(function() { + "use strict"; + + angular.module('angular-carousel') + + .filter('carouselSlice', function() { + return function(collection, start, size) { + if (angular.isArray(collection)) { + return collection.slice(start, start + size); + } else if (angular.isObject(collection)) { + // dont try to slice collections :) + return collection; + } + }; + }); + +})(); diff --git a/www/lib/angular-carousel/dist/angular-carousel.min.css b/www/lib/angular-carousel/dist/angular-carousel.min.css new file mode 100644 index 00000000..976a3cdb --- /dev/null +++ b/www/lib/angular-carousel/dist/angular-carousel.min.css @@ -0,0 +1 @@ +input[type=range]{width:300px}ul[rn-carousel]{overflow:hidden;padding:0;white-space:nowrap;position:relative;-webkit-perspective:1000px;-ms-perspective:1000px;perspective:1000px;-ms-touch-action:pan-y;touch-action:pan-y}ul[rn-carousel]>li{color:#000;-webkit-backface-visibility:hidden;-ms-backface-visibility:hidden;backface-visibility:hidden;overflow:visible;vertical-align:top;position:absolute;left:0;right:0;white-space:normal;padding:0;margin:0;list-style-type:none;width:100%;height:100%;display:inline-block}ul[rn-carousel-buffered]>li{display:none}ul[rn-carousel-transition=hexagon]{overflow:visible}div.rn-carousel-indicator span{cursor:pointer;color:#666}div.rn-carousel-indicator span.active{color:#fff}.rn-carousel-control{-webkit-transition:opacity .2s ease-out;transition:opacity .2s ease-out;font-size:2rem;position:absolute;top:40%;opacity:.75;cursor:pointer}.rn-carousel-control:hover{opacity:1}.rn-carousel-control.rn-carousel-control-prev{left:.5em}.rn-carousel-control.rn-carousel-control-prev:before{content:"<"}.rn-carousel-control.rn-carousel-control-next{right:.5em}.rn-carousel-control.rn-carousel-control-next:before{content:">"} diff --git a/www/lib/angular-carousel/dist/angular-carousel.min.js b/www/lib/angular-carousel/dist/angular-carousel.min.js new file mode 100644 index 00000000..1313d21e --- /dev/null +++ b/www/lib/angular-carousel/dist/angular-carousel.min.js @@ -0,0 +1,8 @@ +/** + * Angular Carousel - Mobile friendly touch carousel for AngularJS + * @version v0.3.12 - 2015-06-11 + * @link http://revolunet.github.com/angular-carousel + * @author Julien Bouquillon <julien@revolunet.com> + * @license MIT License, http://www.opensource.org/licenses/MIT + */ +angular.module("angular-carousel",["ngTouch","angular-carousel.shifty"]),angular.module("angular-carousel").directive("rnCarouselAutoSlide",["$interval",function(a){return{restrict:"A",link:function(b,c,d){var e=function(){b.autoSlider&&(a.cancel(b.autoSlider),b.autoSlider=null)},f=function(){b.autoSlide()};b.$watch("carouselIndex",f),d.hasOwnProperty("rnCarouselPauseOnHover")&&"false"!==d.rnCarouselPauseOnHover&&(c.on("mouseenter",e),c.on("mouseleave",f)),b.$on("$destroy",function(){e(),c.off("mouseenter",e),c.off("mouseleave",f)})}}}]),angular.module("angular-carousel").directive("rnCarouselIndicators",["$parse",function(a){return{restrict:"A",scope:{slides:"=",index:"=rnCarouselIndex"},templateUrl:"carousel-indicators.html",link:function(b,c,d){var e=a(d.rnCarouselIndex);b.goToSlide=function(a){e.assign(b.$parent.$parent,a)}}}}]),angular.module("angular-carousel").run(["$templateCache",function(a){a.put("carousel-indicators.html",'<div class="rn-carousel-indicator">\n<span ng-repeat="slide in slides" ng-class="{active: $index==index}" ng-click="goToSlide($index)">●</span></div>')}]),function(){"use strict";angular.module("angular-carousel").service("DeviceCapabilities",function(){function a(){var a="transform",b="webkitTransform";return"undefined"!=typeof document.body.style[a]?["webkit","moz","o","ms"].every(function(b){var c="-"+b+"-transform";return"undefined"!=typeof document.body.style[c]?(a=c,!1):!0}):a="undefined"!=typeof document.body.style[b]?"-webkit-transform":void 0,a}function b(){var a,b=document.createElement("p"),c={webkitTransform:"-webkit-transform",msTransform:"-ms-transform",transform:"transform"};document.body.insertBefore(b,null);for(var d in c)void 0!==b.style[d]&&(b.style[d]="translate3d(1px,1px,1px)",a=window.getComputedStyle(b).getPropertyValue(c[d]));return document.body.removeChild(b),void 0!==a&&a.length>0&&"none"!==a}return{has3d:b(),transformProperty:a()}}).service("computeCarouselSlideStyle",["DeviceCapabilities",function(a){return function(b,c,d){var e,f={display:"inline-block"},g=100*b+c,h=a.has3d?"translate3d("+g+"%, 0, 0)":"translate3d("+g+"%, 0)",i=(100-Math.abs(g))/100;if(a.transformProperty)if("fadeAndSlide"==d)f[a.transformProperty]=h,e=0,Math.abs(g)<100&&(e=.3+.7*i),f.opacity=e;else if("hexagon"==d){var j=100,k=0,l=60*(i-1);j=-100*b>c?100:0,k=-100*b>c?l:-l,f[a.transformProperty]=h+" rotateY("+k+"deg)",f[a.transformProperty+"-origin"]=j+"% 50%"}else if("zoom"==d){f[a.transformProperty]=h;var m=1;Math.abs(g)<100&&(m=1+2*(1-i)),f[a.transformProperty]+=" scale("+m+")",f[a.transformProperty+"-origin"]="50% 50%",e=0,Math.abs(g)<100&&(e=.3+.7*i),f.opacity=e}else f[a.transformProperty]=h;else f["margin-left"]=g+"%";return f}}]).service("createStyleString",function(){return function(a){var b=[];return angular.forEach(a,function(a,c){b.push(c+":"+a)}),b.join(";")}}).directive("rnCarousel",["$swipe","$window","$document","$parse","$compile","$timeout","$interval","computeCarouselSlideStyle","createStyleString","Tweenable",function(a,b,c,d,e,f,g,h,i,j){function k(a,b,c){var d=c;return a.every(function(a,c){return angular.equals(a,b)?(d=c,!1):!0}),d}{var l=0;b.requestAnimationFrame||b.webkitRequestAnimationFrame||b.mozRequestAnimationFrame}return{restrict:"A",scope:!0,compile:function(m,n){var o,p,q=m[0].querySelector("li"),r=q?q.attributes:[],s=!1,t=!1;return["ng-repeat","data-ng-repeat","ng:repeat","x-ng-repeat"].every(function(a){var b=r[a];if(angular.isDefined(b)){var c=b.value.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),d=c[3];if(o=c[1],p=c[2],o)return angular.isDefined(n.rnCarouselBuffered)&&(t=!0,b.value=o+" in "+p+"|carouselSlice:carouselBufferIndex:carouselBufferSize",d&&(b.value+=" track by "+d)),s=!0,!1}return!0}),function(m,n,o){function q(){return n[0].querySelectorAll("ul[rn-carousel] > li")}function r(a){M=!0,C({x:a.clientX,y:a.clientY},a)}function u(a){var b=100*m.carouselBufferIndex+a;angular.forEach(q(),function(a,c){a.style.cssText=i(h(c,b,J.transitionType))})}function v(a,b){if(void 0===a&&(a=m.carouselIndex),b=b||{},b.animate===!1||"none"===J.transitionType)return R=!1,L=-100*a,m.carouselIndex=a,D(),void 0;R=!0;var c=new j;c.tween({from:{x:L},to:{x:-100*a},duration:J.transitionDuration,easing:J.transitionEasing,step:function(a){u(a.x)},finish:function(){m.$apply(function(){m.carouselIndex=a,L=-100*a,D(),f(function(){R=!1},0,!1)})}})}function w(){var a=n[0].getBoundingClientRect();return a.width?a.width:a.right-a.left}function x(){O=w()}function y(){Q||(Q=!0,c.bind("mouseup",r))}function z(){Q&&(Q=!1,c.unbind("mouseup",r))}function A(a){return R||N.length<=1?void 0:(x(),P=n[0].querySelector("li").getBoundingClientRect().left,F=!0,G=a.x,!1)}function B(a){var b,c;if(y(),F&&(b=a.x,c=G-b,c>2||-2>c)){M=!0;var d=L+100*-c/O;u(d)}return!1}function C(a,b){if((!b||M)&&(z(),F=!1,M=!1,H=G-a.x,0!==H&&!R))if(L+=100*-H/O,J.isSequential){var c=J.moveTreshold*O,e=-H,f=-Math[e>=0?"ceil":"floor"](e/O),g=Math.abs(e)>c;N&&f+m.carouselIndex>=N.length&&(f=N.length-1-m.carouselIndex),f+m.carouselIndex<0&&(f=-m.carouselIndex);var h=g?f:0;H=m.carouselIndex+h,v(H),void 0!==o.rnCarouselOnInfiniteScrollRight&&0===f&&0!==m.carouselIndex&&(d(o.rnCarouselOnInfiniteScrollRight)(m),v(0)),void 0!==o.rnCarouselOnInfiniteScrollLeft&&0===f&&0===m.carouselIndex&&0===h&&(d(o.rnCarouselOnInfiniteScrollLeft)(m),v(N.length))}else m.$apply(function(){m.carouselIndex=parseInt(-L/100,10),D()})}function D(){var a=0,b=(m.carouselBufferSize-1)/2;t?(a=m.carouselIndex<=b?0:N&&N.length<m.carouselBufferSize?0:N&&m.carouselIndex>N.length-m.carouselBufferSize?N.length-m.carouselBufferSize:m.carouselIndex-b,m.carouselBufferIndex=a,f(function(){u(L)},0,!1)):f(function(){u(L)},0,!1)}function E(){x(),v()}l++;var F,G,H,I={transitionType:o.rnCarouselTransition||"slide",transitionEasing:o.rnCarouselEasing||"easeTo",transitionDuration:parseInt(o.rnCarouselDuration,10)||300,isSequential:!0,autoSlideDuration:3,bufferSize:5,moveTreshold:.1,defaultIndex:0},J=angular.extend({},I),K=!1,L=0,M=!1,N=[],O=null,P=null,Q=!1,R=!1;"true"!==o.rnSwipeDisabled&&a.bind(n,{start:A,move:B,end:C,cancel:function(a){C({},a)}}),m.nextSlide=function(a){var b=m.carouselIndex+1;b>N.length-1&&(b=0),R||v(b,a)},m.prevSlide=function(a){var b=m.carouselIndex-1;0>b&&(b=N.length-1),v(b,a)};var S=!0;if(m.carouselIndex=0,s||(N=[],angular.forEach(q(),function(a,b){N.push({id:b})})),void 0!==o.rnCarouselControls){var T=s?p.replace("::","")+".length - 1":N.length-1,U='<div class="rn-carousel-controls">\n <span class="rn-carousel-control rn-carousel-control-prev" ng-click="prevSlide()" ng-if="carouselIndex > 0"></span>\n <span class="rn-carousel-control rn-carousel-control-next" ng-click="nextSlide()" ng-if="carouselIndex < '+T+'"></span>\n</div>';n.parent().append(e(angular.element(U))(m))}if(void 0!==o.rnCarouselAutoSlide){var V=parseInt(o.rnCarouselAutoSlide,10)||J.autoSlideDuration;m.autoSlide=function(){m.autoSlider&&(g.cancel(m.autoSlider),m.autoSlider=null),m.autoSlider=g(function(){R||F||m.nextSlide()},1e3*V)}}if(o.rnCarouselDefaultIndex){var W=d(o.rnCarouselDefaultIndex);J.defaultIndex=W(m.$parent)||0}if(o.rnCarouselIndex){var X=function(a){Y.assign(m.$parent,a)},Y=d(o.rnCarouselIndex);angular.isFunction(Y.assign)?(m.$watch("carouselIndex",function(a){X(a)}),m.$parent.$watch(Y,function(a){void 0!==a&&null!==a&&(N&&N.length>0&&a>=N.length?(a=N.length-1,X(a)):N&&0>a&&(a=0,X(a)),R||v(a,{animate:!S}),S=!1)}),K=!0,J.defaultIndex&&v(J.defaultIndex,{animate:!S})):isNaN(o.rnCarouselIndex)||v(parseInt(o.rnCarouselIndex,10),{animate:!1})}else v(J.defaultIndex,{animate:!S}),S=!1;if(o.rnCarouselLocked&&m.$watch(o.rnCarouselLocked,function(a){R=a===!0?!0:!1}),s){var Z=void 0!==o.rnCarouselDeepWatch;m[Z?"$watch":"$watchCollection"](p,function(a,b){if(N=a,Z&&angular.isArray(a)){var c=b[m.carouselIndex],d=k(a,c,m.carouselIndex);v(d,{animate:!1})}else v(m.carouselIndex,{animate:!1})},!0)}m.$on("$destroy",function(){z()}),m.carouselBufferIndex=0,m.carouselBufferSize=J.bufferSize;var $=angular.element(b);$.bind("orientationchange",E),$.bind("resize",E),m.$on("$destroy",function(){z(),$.unbind("orientationchange",E),$.unbind("resize",E)})}}}}])}(),angular.module("angular-carousel.shifty",[]).factory("Tweenable",function(){return function(a){var b=function(){"use strict";function b(){}function c(a,b){var c;for(c in a)Object.hasOwnProperty.call(a,c)&&b(c)}function d(a,b){return c(b,function(c){a[c]=b[c]}),a}function e(a,b){c(b,function(c){"undefined"==typeof a[c]&&(a[c]=b[c])})}function f(a,b,c,d,e,f,h){var i,j=(a-f)/e;for(i in b)b.hasOwnProperty(i)&&(b[i]=g(c[i],d[i],l[h[i]],j));return b}function g(a,b,c,d){return a+(b-a)*c(d)}function h(a,b){var d=k.prototype.filter,e=a._filterArgs;c(d,function(c){"undefined"!=typeof d[c][b]&&d[c][b].apply(a,e)})}function i(a,b,c,d,e,g,i,j,k){s=b+c,t=Math.min(r(),s),u=t>=s,v=c-(s-t),a.isPlaying()&&!u?(a._scheduleId=k(a._timeoutHandler,p),h(a,"beforeTween"),f(t,d,e,g,c,b,i),h(a,"afterTween"),j(d,a._attachment,v)):u&&(j(g,a._attachment,v),a.stop(!0))}function j(a,b){var d={};return"string"==typeof b?c(a,function(a){d[a]=b}):c(a,function(a){d[a]||(d[a]=b[a]||n)}),d}function k(a,b){this._currentState=a||{},this._configured=!1,this._scheduleFunction=m,"undefined"!=typeof b&&this.setConfig(b)}var l,m,n="linear",o=500,p=1e3/60,q=Date.now?Date.now:function(){return+new Date},r="undefined"!=typeof SHIFTY_DEBUG_NOW?SHIFTY_DEBUG_NOW:q;m="undefined"!=typeof window?window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||window.mozCancelRequestAnimationFrame&&window.mozRequestAnimationFrame||setTimeout:setTimeout;var s,t,u,v;return k.prototype.tween=function(a){return this._isTweening?this:(void 0===a&&this._configured||this.setConfig(a),this._timestamp=r(),this._start(this.get(),this._attachment),this.resume())},k.prototype.setConfig=function(a){a=a||{},this._configured=!0,this._attachment=a.attachment,this._pausedAtTime=null,this._scheduleId=null,this._start=a.start||b,this._step=a.step||b,this._finish=a.finish||b,this._duration=a.duration||o,this._currentState=a.from||this.get(),this._originalState=this.get(),this._targetState=a.to||this.get();var c=this._currentState,d=this._targetState;return e(d,c),this._easing=j(c,a.easing||n),this._filterArgs=[c,this._originalState,d,this._easing],h(this,"tweenCreated"),this},k.prototype.get=function(){return d({},this._currentState)},k.prototype.set=function(a){this._currentState=a},k.prototype.pause=function(){return this._pausedAtTime=r(),this._isPaused=!0,this},k.prototype.resume=function(){this._isPaused&&(this._timestamp+=r()-this._pausedAtTime),this._isPaused=!1,this._isTweening=!0;var a=this;return this._timeoutHandler=function(){i(a,a._timestamp,a._duration,a._currentState,a._originalState,a._targetState,a._easing,a._step,a._scheduleFunction)},this._timeoutHandler(),this},k.prototype.seek=function(a){return this._timestamp=r()-a,this.isPlaying()||(this._isTweening=!0,this._isPaused=!1,i(this,this._timestamp,this._duration,this._currentState,this._originalState,this._targetState,this._easing,this._step,this._scheduleFunction),this._timeoutHandler(),this.pause()),this},k.prototype.stop=function(c){return this._isTweening=!1,this._isPaused=!1,this._timeoutHandler=b,(a.cancelAnimationFrame||a.webkitCancelAnimationFrame||a.oCancelAnimationFrame||a.msCancelAnimationFrame||a.mozCancelRequestAnimationFrame||a.clearTimeout)(this._scheduleId),c&&(d(this._currentState,this._targetState),h(this,"afterTweenEnd"),this._finish.call(this,this._currentState,this._attachment)),this},k.prototype.isPlaying=function(){return this._isTweening&&!this._isPaused},k.prototype.setScheduleFunction=function(a){this._scheduleFunction=a},k.prototype.dispose=function(){var a;for(a in this)this.hasOwnProperty(a)&&delete this[a]},k.prototype.filter={},k.prototype.formula={linear:function(a){return a}},l=k.prototype.formula,d(k,{now:r,each:c,tweenProps:f,tweenProp:g,applyFilter:h,shallowCopy:d,defaults:e,composeEasingObject:j}),a.Tweenable=k,k}();!function(){b.shallowCopy(b.prototype.formula,{easeInQuad:function(a){return Math.pow(a,2)},easeOutQuad:function(a){return-(Math.pow(a-1,2)-1)},easeInOutQuad:function(a){return(a/=.5)<1?.5*Math.pow(a,2):-.5*((a-=2)*a-2)},easeInCubic:function(a){return Math.pow(a,3)},easeOutCubic:function(a){return Math.pow(a-1,3)+1},easeInOutCubic:function(a){return(a/=.5)<1?.5*Math.pow(a,3):.5*(Math.pow(a-2,3)+2)},easeInQuart:function(a){return Math.pow(a,4)},easeOutQuart:function(a){return-(Math.pow(a-1,4)-1)},easeInOutQuart:function(a){return(a/=.5)<1?.5*Math.pow(a,4):-.5*((a-=2)*Math.pow(a,3)-2)},easeInQuint:function(a){return Math.pow(a,5)},easeOutQuint:function(a){return Math.pow(a-1,5)+1},easeInOutQuint:function(a){return(a/=.5)<1?.5*Math.pow(a,5):.5*(Math.pow(a-2,5)+2)},easeInSine:function(a){return-Math.cos(a*(Math.PI/2))+1},easeOutSine:function(a){return Math.sin(a*(Math.PI/2))},easeInOutSine:function(a){return-.5*(Math.cos(Math.PI*a)-1)},easeInExpo:function(a){return 0===a?0:Math.pow(2,10*(a-1))},easeOutExpo:function(a){return 1===a?1:-Math.pow(2,-10*a)+1},easeInOutExpo:function(a){return 0===a?0:1===a?1:(a/=.5)<1?.5*Math.pow(2,10*(a-1)):.5*(-Math.pow(2,-10*--a)+2)},easeInCirc:function(a){return-(Math.sqrt(1-a*a)-1)},easeOutCirc:function(a){return Math.sqrt(1-Math.pow(a-1,2))},easeInOutCirc:function(a){return(a/=.5)<1?-.5*(Math.sqrt(1-a*a)-1):.5*(Math.sqrt(1-(a-=2)*a)+1)},easeOutBounce:function(a){return 1/2.75>a?7.5625*a*a:2/2.75>a?7.5625*(a-=1.5/2.75)*a+.75:2.5/2.75>a?7.5625*(a-=2.25/2.75)*a+.9375:7.5625*(a-=2.625/2.75)*a+.984375},easeInBack:function(a){var b=1.70158;return a*a*((b+1)*a-b)},easeOutBack:function(a){var b=1.70158;return(a-=1)*a*((b+1)*a+b)+1},easeInOutBack:function(a){var b=1.70158;return(a/=.5)<1?.5*a*a*(((b*=1.525)+1)*a-b):.5*((a-=2)*a*(((b*=1.525)+1)*a+b)+2)},elastic:function(a){return-1*Math.pow(4,-8*a)*Math.sin(2*(6*a-1)*Math.PI/2)+1},swingFromTo:function(a){var b=1.70158;return(a/=.5)<1?.5*a*a*(((b*=1.525)+1)*a-b):.5*((a-=2)*a*(((b*=1.525)+1)*a+b)+2)},swingFrom:function(a){var b=1.70158;return a*a*((b+1)*a-b)},swingTo:function(a){var b=1.70158;return(a-=1)*a*((b+1)*a+b)+1},bounce:function(a){return 1/2.75>a?7.5625*a*a:2/2.75>a?7.5625*(a-=1.5/2.75)*a+.75:2.5/2.75>a?7.5625*(a-=2.25/2.75)*a+.9375:7.5625*(a-=2.625/2.75)*a+.984375},bouncePast:function(a){return 1/2.75>a?7.5625*a*a:2/2.75>a?2-(7.5625*(a-=1.5/2.75)*a+.75):2.5/2.75>a?2-(7.5625*(a-=2.25/2.75)*a+.9375):2-(7.5625*(a-=2.625/2.75)*a+.984375)},easeFromTo:function(a){return(a/=.5)<1?.5*Math.pow(a,4):-.5*((a-=2)*Math.pow(a,3)-2)},easeFrom:function(a){return Math.pow(a,4)},easeTo:function(a){return Math.pow(a,.25)}})}(),function(){function a(a,b,c,d,e,f){function g(a){return((n*a+o)*a+p)*a}function h(a){return((q*a+r)*a+s)*a}function i(a){return(3*n*a+2*o)*a+p}function j(a){return 1/(200*a)}function k(a,b){return h(m(a,b))}function l(a){return a>=0?a:0-a}function m(a,b){var c,d,e,f,h,j;for(e=a,j=0;8>j;j++){if(f=g(e)-a,l(f)<b)return e;if(h=i(e),l(h)<1e-6)break;e-=f/h}if(c=0,d=1,e=a,c>e)return c;if(e>d)return d;for(;d>c;){if(f=g(e),l(f-a)<b)return e;a>f?c=e:d=e,e=.5*(d-c)+c}return e}var n=0,o=0,p=0,q=0,r=0,s=0;return p=3*b,o=3*(d-b)-p,n=1-p-o,s=3*c,r=3*(e-c)-s,q=1-s-r,k(a,j(f))}function c(b,c,d,e){return function(f){return a(f,b,c,d,e,1)}}b.setBezierFunction=function(a,d,e,f,g){var h=c(d,e,f,g);return h.x1=d,h.y1=e,h.x2=f,h.y2=g,b.prototype.formula[a]=h},b.unsetBezierFunction=function(a){delete b.prototype.formula[a]}}(),function(){function a(a,c,d,e,f){return b.tweenProps(e,c,a,d,1,0,f)}var c=new b;c._filterArgs=[],b.interpolate=function(d,e,f,g){var h=b.shallowCopy({},d),i=b.composeEasingObject(d,g||"linear");c.set({});var j=c._filterArgs;j.length=0,j[0]=h,j[1]=d,j[2]=e,j[3]=i,b.applyFilter(c,"tweenCreated"),b.applyFilter(c,"beforeTween");var k=a(d,h,e,f,i);return b.applyFilter(c,"afterTween"),k}}(),function(a){function b(a,b){B.length=0;var c,d=a.length;for(c=0;d>c;c++)B.push("_"+b+"_"+c);return B}function c(a){var b=a.match(v);return b?(1===b.length||a[0].match(u))&&b.unshift(""):b=["",""],b.join(A)}function d(b){a.each(b,function(a){var c=b[a];"string"==typeof c&&c.match(z)&&(b[a]=e(c))})}function e(a){return i(z,a,f)}function f(a){var b=g(a);return"rgb("+b[0]+","+b[1]+","+b[2]+")"}function g(a){return a=a.replace(/#/,""),3===a.length&&(a=a.split(""),a=a[0]+a[0]+a[1]+a[1]+a[2]+a[2]),C[0]=h(a.substr(0,2)),C[1]=h(a.substr(2,2)),C[2]=h(a.substr(4,2)),C}function h(a){return parseInt(a,16)}function i(a,b,c){var d=b.match(a),e=b.replace(a,A);if(d)for(var f,g=d.length,h=0;g>h;h++)f=d.shift(),e=e.replace(A,c(f));return e}function j(a){return i(x,a,k)}function k(a){for(var b=a.match(w),c=b.length,d=a.match(y)[0],e=0;c>e;e++)d+=parseInt(b[e],10)+",";return d=d.slice(0,-1)+")"}function l(d){var e={};return a.each(d,function(a){var f=d[a];if("string"==typeof f){var g=r(f);e[a]={formatString:c(f),chunkNames:b(g,a)}}}),e}function m(b,c){a.each(c,function(a){for(var d=b[a],e=r(d),f=e.length,g=0;f>g;g++)b[c[a].chunkNames[g]]=+e[g];delete b[a]})}function n(b,c){a.each(c,function(a){var d=b[a],e=o(b,c[a].chunkNames),f=p(e,c[a].chunkNames);d=q(c[a].formatString,f),b[a]=j(d)})}function o(a,b){for(var c,d={},e=b.length,f=0;e>f;f++)c=b[f],d[c]=a[c],delete a[c];return d}function p(a,b){D.length=0;for(var c=b.length,d=0;c>d;d++)D.push(a[b[d]]);return D}function q(a,b){for(var c=a,d=b.length,e=0;d>e;e++)c=c.replace(A,+b[e].toFixed(4));return c}function r(a){return a.match(w)}function s(b,c){a.each(c,function(a){for(var d=c[a],e=d.chunkNames,f=e.length,g=b[a].split(" "),h=g[g.length-1],i=0;f>i;i++)b[e[i]]=g[i]||h;delete b[a]})}function t(b,c){a.each(c,function(a){for(var d=c[a],e=d.chunkNames,f=e.length,g="",h=0;f>h;h++)g+=" "+b[e[h]],delete b[e[h]];b[a]=g.substr(1)})}var u=/(\d|\-|\.)/,v=/([^\-0-9\.]+)/g,w=/[0-9.\-]+/g,x=new RegExp("rgb\\("+w.source+/,\s*/.source+w.source+/,\s*/.source+w.source+"\\)","g"),y=/^.*\(/,z=/#([0-9]|[a-f]){3,6}/gi,A="VAL",B=[],C=[],D=[];a.prototype.filter.token={tweenCreated:function(a,b,c){d(a),d(b),d(c),this._tokenData=l(a)},beforeTween:function(a,b,c,d){s(d,this._tokenData),m(a,this._tokenData),m(b,this._tokenData),m(c,this._tokenData)},afterTween:function(a,b,c,d){n(a,this._tokenData),n(b,this._tokenData),n(c,this._tokenData),t(d,this._tokenData)}}}(b)}(window),window.Tweenable}),function(){"use strict";angular.module("angular-carousel").filter("carouselSlice",function(){return function(a,b,c){return angular.isArray(a)?a.slice(b,b+c):angular.isObject(a)?a:void 0}})}(); diff --git a/www/lib/angular-carousel/fullscreen.html b/www/lib/angular-carousel/fullscreen.html new file mode 100644 index 00000000..0cc8c9b3 --- /dev/null +++ b/www/lib/angular-carousel/fullscreen.html @@ -0,0 +1,74 @@ +<!DOCTYPE html> +<html ng-app="DemoApp"> + <head> + <meta charset="UTF-8"> + <title>angular-carousel demo</title> + <meta name="viewport" content="width=620, user-scalable=no"> + <link href='http://fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'> + <link href='./dist/angular-carousel.css' rel='stylesheet' type='text/css'> + <link href='./demo/demo.css' rel='stylesheet' type='text/css'> + <style> + html, body{ + margin:0; + padding:0; + } + </style> + </head> + <body ng-controller="DemoCtrl"> + <div class="carousel-demo-fullscreen"> + <ul rn-carousel rn-carousel-controls rn-carousel-transition="hexagon" rn-carousel-index="carouselIndex" rn-carousel-buffered class="carousel1"> + <li ng-repeat="slide in slides track by slide.id" ng-class="'id-' + slide.id"> + <div ng-style="{'background-image': 'url(' + slide.img + ')'}" class="bgimage"> + #{{ slide.id }} + </div> + </li> + </ul> + </div> + + </body> + <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular.min.js"></script> + <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular-touch.min.js"></script> + + <script src="./dist/angular-carousel.min.js"></script> + <!--<script src="./src/angular-carousel.js"></script> + <script src="./src/directives/rn-carousel.js"></script> + <script src="./src/directives/rn-carousel-indicators.js"></script> + <script src="./src/directives/sliceFilter.js"></script> + <script src="./src/directives/shifty.js"></script> + --> + <script> + angular.module('DemoApp', [ + 'angular-carousel' + ]) + + .controller('DemoCtrl', function($scope) { + + $scope.colors = ["#fc0003", "#f70008", "#f2000d", "#ed0012", "#e80017", "#e3001c", "#de0021", "#d90026", "#d4002b", "#cf0030", "#c90036", "#c4003b", "#bf0040", "#ba0045", "#b5004a", "#b0004f", "#ab0054", "#a60059", "#a1005e", "#9c0063", "#960069", "#91006e", "#8c0073", "#870078", "#82007d", "#7d0082", "#780087", "#73008c", "#6e0091", "#690096", "#63009c", "#5e00a1", "#5900a6", "#5400ab", "#4f00b0", "#4a00b5", "#4500ba", "#4000bf", "#3b00c4", "#3600c9", "#3000cf", "#2b00d4", "#2600d9", "#2100de", "#1c00e3", "#1700e8", "#1200ed", "#0d00f2", "#0800f7", "#0300fc"]; + + function addSlide(target, style) { + var i = target.length; + target.push({ + id: (i + 1), + label: 'slide #' + (i + 1), + img: 'http://lorempixel.com/450/300/' + style + '/' + ((i + 1) % 10) , + color: $scope.colors[ (i*10) % $scope.colors.length], + odd: (i % 2 === 0) + }); + }; + + $scope.carouselIndex = 3; + + function addSlides(target, style, qty) { + for (var i=0; i < qty; i++) { + addSlide(target, style); + } + } + + // 1st ngRepeat demo + $scope.slides = []; + addSlides($scope.slides, 'sports', 50); + + }); + + </script> +</html> diff --git a/www/lib/angular-carousel/game.html b/www/lib/angular-carousel/game.html new file mode 100644 index 00000000..a346fed0 --- /dev/null +++ b/www/lib/angular-carousel/game.html @@ -0,0 +1,91 @@ +<!DOCTYPE html> +<html ng-app="DemoApp"> + <head> + <meta charset="UTF-8"> + <title>angular-carousel demo</title> + <meta name="viewport" content="width=620, user-scalable=no"> + <link href='http://fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'> + <link href='./dist/angular-carousel.css' rel='stylesheet' type='text/css'> + <link href='./demo/demo.css' rel='stylesheet' type='text/css'> + <style> + html, body{ + margin:0; + padding:0; + } + .carousel-demo-game { + height: 100%; + } + + .carousel-demo-game ul[rn-carousel] { + overflow:hidden; + width:25%; + height:25%; + margin:0; + display:inline-block; + } + </style> + </head> + <body ng-controller="DemoCtrl"> + <div class="carousel-demo-game"> + <ul ng-repeat="carousel in carousels" rn-carousel rn-carousel-transition="hexagon" rn-carousel-buffered class="carousel1"> + <li ng-repeat="slide in slides track by slide.id" ng-class="'id-' + slide.id"> + <div ng-style="{'background-image': 'url(' + slide.img + ')'}" class="bgimage"> + #{{ slide.id }} + </div> + </li> + </ul> + </div> + + </body> + <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular.min.js"></script> + <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular-touch.min.js"></script> + + <script src="./dist/angular-carousel.min.js"></script> + <!--<script src="./src/angular-carousel.js"></script> + <script src="./src/directives/rn-carousel.js"></script> + <script src="./src/directives/rn-carousel-indicators.js"></script> + <script src="./src/directives/sliceFilter.js"></script> + <script src="./src/directives/shifty.js"></script> + --> + <script> + angular.module('DemoApp', [ + 'angular-carousel' + ]) + + .controller('DemoCtrl', function($scope) { + + $scope.carousels = []; + for (var i=0; i<16;i++) { + $scope.carousels.push({ + id:i + }); + } + $scope.colors = ["#fc0003", "#f70008", "#f2000d", "#ed0012", "#e80017", "#e3001c", "#de0021", "#d90026", "#d4002b", "#cf0030", "#c90036", "#c4003b", "#bf0040", "#ba0045", "#b5004a", "#b0004f", "#ab0054", "#a60059", "#a1005e", "#9c0063", "#960069", "#91006e", "#8c0073", "#870078", "#82007d", "#7d0082", "#780087", "#73008c", "#6e0091", "#690096", "#63009c", "#5e00a1", "#5900a6", "#5400ab", "#4f00b0", "#4a00b5", "#4500ba", "#4000bf", "#3b00c4", "#3600c9", "#3000cf", "#2b00d4", "#2600d9", "#2100de", "#1c00e3", "#1700e8", "#1200ed", "#0d00f2", "#0800f7", "#0300fc"]; + + function addSlide(target, style) { + var i = target.length; + target.push({ + id: (i + 1), + label: 'slide #' + (i + 1), + img: 'http://lorempixel.com/450/300/' + style + '/' + ((i + 1) % 10) , + color: $scope.colors[ (i*10) % $scope.colors.length], + odd: (i % 2 === 0) + }); + }; + + $scope.carouselIndex = 3; + + function addSlides(target, style, qty) { + for (var i=0; i < qty; i++) { + addSlide(target, style); + } + } + + // 1st ngRepeat demo + $scope.slides = []; + addSlides($scope.slides, 'sports', 50); + + }); + + </script> +</html> diff --git a/www/lib/angular-carousel/index.html b/www/lib/angular-carousel/index.html new file mode 100644 index 00000000..4ed5efde --- /dev/null +++ b/www/lib/angular-carousel/index.html @@ -0,0 +1,331 @@ +<!DOCTYPE html> +<html ng-app="DemoApp"> + <head> + <meta charset="UTF-8"> + <title>angular-carousel demo</title> + <meta name="viewport" content="width=620, user-scalable=no"> + <link href='http://fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'> + <link href='./dist/angular-carousel.css' rel='stylesheet' type='text/css'> + <link href='./demo/demo.css' rel='stylesheet' type='text/css'> + </head> + <body ng-controller="DemoCtrl"> + <a href="https://github.com/revolunet/angular-carousel"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub"></a> + + <h1>AngularJS Touch Carousel</h1> + <div class='intro'> + Transform your ng-repeat or DOM nodes in a mobile-friendly carousel just by adding a 'rn-carousel' attribute to your HTML; AngularJS directives FTW :)<br><br> + Carousels are data-bound to your ngRepeat collections and can be DOM buffered (good for performance) + <br><br> + Swipe these demos with your mouse or finger + </div> + + <div> + <div> + <h3>Buffered ngRepeat demo</h3> + <div class="details">A simple buffered ng-repeat with a custom template. + <br> + Swipe 50 slides with only 5 slides in the DOM. use builtin controls + </div> + <div class="carousel-demo"> + index : <input type="number" class="tiny" ng-model="carouselIndex"> + <ul rn-carousel rn-carousel-controls rn-carousel-index="carouselIndex" rn-carousel-buffered class="carousel1"> + <li ng-repeat="slide in slides track by slide.id" ng-class="'id-' + slide.id"> + <div ng-style="{'background-image': 'url(' + slide.img + ')'}" class="bgimage"> + #{{ slide.id }} + </div> + </li> + </ul> + </div> + </div> + <div> + <h3>buffered ngRepeat with auto-slide(pause on hover) and builtin indicators </h3> + <div class="details"> + </div> + <div class="carousel-demo"> + index: <input type="number" class="tiny" ng-model="carouselIndex2"> + <ul rn-carousel rn-carousel-index="carouselIndex2" rn-carousel-auto-slide rn-carousel-pause-on-hover rn-carousel-buffered class="carousel2"> + <li ng-repeat="slide in slides2 track by slide.id" ng-class="'id-' + slide.id"> + <div ng-style="{'background-image': 'url(' + slide.img + ')'}" class="bgimage"> + #{{ slide.id }} + </div> + </li> + </ul> + <div rn-carousel-indicators ng-if="slides2.length > 1" slides="slides2" rn-carousel-index="carouselIndex2"></div> + </div> + </div> + <br> + <div> + <h3>buffered ngRepeat and custom indicators </h3> + <div class="details"> + </div> + <div class="carousel-demo"> + <ul rn-carousel rn-carousel-index="carouselIndex22" rn-carousel-auto-slide rn-carousel-buffered class="carousel2"> + <li ng-repeat="slide in slides2 track by slide.id" ng-class="'id-' + slide.id"> + <div ng-style="{'background-image': 'url(' + slide.img + ')'}" class="bgimage"> + #{{ slide.id }} + </div> + </li> + </ul> + <div class="rn-carousel-indicator custom-indicator"> + <span ng-repeat="slide in slides2" ng-class="{active: $index==$parent.carouselIndex22}" ng-click="$parent.carouselIndex22 = $index">★</span> + </div> + </div> + </div> + <br> + <div > + <h3>buffered ngRepeat with custom transition</h3> + <div class="details">Use the 'hexagon' transition. index is shared with the carousel below. + </div> + <div class="carousel-demo"> + <ul rn-carousel rn-carousel-index="carouselIndex3" rn-carousel-transition="hexagon" rn-carousel-buffered class="carousel3"> + <li ng-repeat="slide in slides3 track by slide.id"> + <div ng-style="{'background-image': 'url(' + slide.img + ')'}" class="bgimage"> + #{{ slide.id }} + </div> + </li> + </ul> + </div> + </div> + <br> + <div> + <h3>Custom templates without ng-repeat and auto-slide</h3> + <div class="details"> + </div> + <div class="carousel-demo"> + <ul rn-carousel rn-carousel-index="3" rn-carousel-auto-slide="3" class="carousel5"> + <li>Slide 1</li> + <li>Slide 2</li> + <li>Slide 3</li> + <li>Slide 4</li> + <li>Slide 5</li> + <li>Slide 6</li> + <li>Slide 7</li> + <li>Slide 8</li> + <li>Slide 9</li> + </ul> + </div> + </div> + <br> + <div> + <h3>Lockable carousel</h3> + <div class="details"> + use rn-carousel-locked binding to dynamically lock/unlock the carousel + </div> + <div class="carousel-demo"> + <label><input type="checkbox" ng-model="isLocked"/>Lock the carousel</label> + <ul rn-carousel class="carousel5" rn-carousel-locked="isLocked"> + <li ng-repeat="slide in slides3 track by slide.id"> + <div ng-style="{'background-image': 'url(' + slide.img + ')'}" class="bgimage"> + #{{ slide.id }} + </div> + </li> + </ul> + </div> + </div> + <br> + <div> + <h3>Add/Remove items in the collection</h3> + <div class="details"> + The carousel is bound to your ngRepeat collection.<br> + If you add items at the start of the collection, then by default, the slide position will change.<br> + If you want to preserve position even when adding items at the head of the collection, you need to add the `rn-carousel-deep-watch` attribute. This has a performance impact so use carefully.<br> + Works great with buffering too so you can have almost infinite slides while keeping only 5 items in the DOM<br> + </div> + <div class="carousel-demo" > + <button ng-click="addSlide('head')">Add at beginning</button> + <button ng-click="addSlide('tail')">Add at end</button> + <ul rn-carousel rn-carousel-index="carouselIndex6" rn-carousel-deep-watch rn-carousel-buffered class="carousel5"> + <li ng-repeat="slide in slides6 track by slide.id"> + <div ng-style="{'background-image': 'url(' + slide.img + ')'}" class="bgimage"> + #{{ slide.id }} + </div> + </li> + </ul> + <div rn-carousel-indicators ng-if="slides6.length > 1" slides="slides6" rn-carousel-index="carouselIndex6"></div> + </div> + </div> + <br> + <div> + <h3>End to End Swiping</h3> + <div class="details"> + The carousel is bound to your ngRepeat collection.<br> + Total images: {{ totalimg }} <br> + Images in one set: {{ setOfImagesToShow }} <br> + Total sets of images: {{ totalimg / setOfImagesToShow | number:0}}<br> + <br> + </div> + <div class="carousel-demo" > + + <ul rn-carousel rn-carousel-index="carouselIndex7" rn-carousel-buffered rn-carousel-on-infinite-scroll-right="loadNextImages()" rn-carousel-on-infinite-scroll-left="loadPreviousImages()" class="carousel5"> + <li ng-repeat="slide in slides7 track by slide.id"> + <div ng-style="{'background-image': 'url(' + slide.img + ')'}" class="bgimage"> + #{{ slide.id }} + </div> + </li> + </ul> + <div rn-carousel-indicators ng-if="slides7.length > 1" slides="slides7" rn-carousel-index="carouselIndex7"></div> + </div> + </div> + + </div> + <p> </p> + <p> </p> + </body> + <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular.min.js"></script> + <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular-touch.min.js"></script> + + <script src="./dist/angular-carousel.min.js"></script> + <!--<script src="./src/angular-carousel.js"></script> + <script src="./src/directives/rn-carousel.js"></script> + <script src="./src/directives/rn-carousel-indicators.js"></script> + <script src="./src/directives/sliceFilter.js"></script> + <script src="./src/directives/shifty.js"></script> + --> + <script> + angular.module('DemoApp', [ + 'angular-carousel' + ]) + + .controller('DemoCtrl', function($scope) { + + $scope.colors = ["#fc0003", "#f70008", "#f2000d", "#ed0012", "#e80017", "#e3001c", "#de0021", "#d90026", "#d4002b", "#cf0030", "#c90036", "#c4003b", "#bf0040", "#ba0045", "#b5004a", "#b0004f", "#ab0054", "#a60059", "#a1005e", "#9c0063", "#960069", "#91006e", "#8c0073", "#870078", "#82007d", "#7d0082", "#780087", "#73008c", "#6e0091", "#690096", "#63009c", "#5e00a1", "#5900a6", "#5400ab", "#4f00b0", "#4a00b5", "#4500ba", "#4000bf", "#3b00c4", "#3600c9", "#3000cf", "#2b00d4", "#2600d9", "#2100de", "#1c00e3", "#1700e8", "#1200ed", "#0d00f2", "#0800f7", "#0300fc"]; + + function getSlide(target, style) { + var i = target.length; + return { + id: (i + 1), + label: 'slide #' + (i + 1), + img: 'http://lorempixel.com/450/300/' + style + '/' + ((i + 1) % 10) , + color: $scope.colors[ (i*10) % $scope.colors.length], + odd: (i % 2 === 0) + }; + } + + function addSlide(target, style) { + target.push(getSlide(target, style)); + }; + + $scope.carouselIndex = 3; + $scope.carouselIndex2 = 0; + $scope.carouselIndex2 = 1; + $scope.carouselIndex3 = 5; + $scope.carouselIndex4 = 5; + + function addSlides(target, style, qty) { + for (var i=0; i < qty; i++) { + addSlide(target, style); + } + } + + // 1st ngRepeat demo + $scope.slides = []; + addSlides($scope.slides, 'sports', 50); + + // 2nd ngRepeat demo + $scope.slides2 = []; + addSlides($scope.slides2, 'sports', 10); + + // 3rd ngRepeat demo + $scope.slides3 = []; + addSlides($scope.slides3, 'people', 50); + + // 4th ngRepeat demo + $scope.slides4 = []; + addSlides($scope.slides4, 'city', 50); + + + // 5th ngRepeat demo + $scope.slides6 = []; + $scope.carouselIndex6 = 0; + addSlides($scope.slides6, 'sports', 10); + $scope.addSlide = function(at) { + if(at==='head') { + $scope.slides6.unshift(getSlide($scope.slides6, 'people')); + } else { + $scope.slides6.push(getSlide($scope.slides6, 'people')); + } + } + + // End to End swiping + // load 130 images in main javascript container + var slideImages = []; + addSlides(slideImages, 'sports', 10); + addSlides(slideImages, 'people', 10); + addSlides(slideImages, 'city', 10); + addSlides(slideImages, 'abstract', 10); + addSlides(slideImages, 'nature', 10); + addSlides(slideImages, 'food', 10); + addSlides(slideImages, 'transport', 10); + addSlides(slideImages, 'animals', 10); + addSlides(slideImages, 'business', 10); + addSlides(slideImages, 'nightlife', 10); + addSlides(slideImages, 'cats', 10); + addSlides(slideImages, 'fashion', 10); + addSlides(slideImages, 'technics', 10); + $scope.totalimg = slideImages.length; + $scope.galleryNumber = 1; + console.log($scope.galleryNumber); + + function getImage(target) { + var i = target.length + , p = (($scope.galleryNumber-1)*$scope.setOfImagesToShow)+i; + console.log("i=" + i + "--" + p); + + return slideImages[p]; + } + function addImages(target, qty) { + + for (var i=0; i < qty; i++) { + addImage(target); + } + } + + function addImage(target) { + target.push(getImage(target)); + } + + $scope.slides7 = []; + $scope.carouselIndex7 = 0; + $scope.setOfImagesToShow = 3; + addImages($scope.slides7, $scope.setOfImagesToShow); + $scope.loadNextImages = function() { + console.log("loading Next images"); + if (slideImages[slideImages.length-1].id !== $scope.slides7[$scope.slides7.length-1].id) { + // Go to next set of images if exist + $scope.slides7 = []; + $scope.carouselIndex7 = 0; + ++$scope.galleryNumber; + addImages($scope.slides7, $scope.setOfImagesToShow); + } else { + // Go to first set of images if not exist + $scope.galleryNumber = 1; + $scope.slides7 = []; + $scope.carouselIndex7 = 0; + addImages($scope.slides7, $scope.setOfImagesToShow); + } + } + $scope.loadPreviousImages = function() { + if (slideImages[0].id !== $scope.slides7[0].id) { + // Go to previous set of images if exist + $scope.slides7 = []; + $scope.carouselIndex7 = 0; + --$scope.galleryNumber; + addImages($scope.slides7, $scope.setOfImagesToShow); + } else { + // Go to last set of images if not exist + console.log("slideimageslength: " + slideImages.length + ", " + slideImages.length-1 / $scope.setOfImagesToShow); + // console.log("slideimageslength: " + slideImages.length ); + $scope.galleryNumber = slideImages.length / $scope.setOfImagesToShow; + $scope.slides7 = []; + $scope.carouselIndex7 = 0; + addImages($scope.slides7, $scope.setOfImagesToShow); + console.log("no images left"); + } + + } + + }) + + + </script> +</html> diff --git a/www/lib/angular-carousel/lib/angular-mobile.js b/www/lib/angular-carousel/lib/angular-mobile.js new file mode 100644 index 00000000..e2c56b55 --- /dev/null +++ b/www/lib/angular-carousel/lib/angular-mobile.js @@ -0,0 +1,470 @@ +/** + * @license AngularJS v1.1.5-3814986 + * (c) 2010-2012 Google, Inc. http://angularjs.org + * License: MIT + */ +(function(window, angular, undefined) { +'use strict'; + +/** + * @ngdoc overview + * @name ngMobile + * @description + * Touch events and other mobile helpers. + * Based on jQuery Mobile touch event handling (jquerymobile.com) + */ + +// define ngMobile module +var ngMobile = angular.module('ngMobile', []); + +/** + * A service for abstracting swipe behavior. Deliberately internal; it is only intended for use in + * ngSwipeLeft/Right and ngCarousel. + * + * Determining whether the user is swiping or scrolling, and handling both mouse and touch events, + * make writing swipe code challenging. This service allows setting callbacks on the start, + * movement and completion of a swipe gesture, without worrying about the complications. + * + */ + +ngMobile.factory('$swipe', [function() { + // The total distance in any direction before we make the call on swipe vs. scroll. + var MOVE_BUFFER_RADIUS = 10; + + // Absolute total movement, used to control swipe vs. scroll. + var totalX, totalY; + // Coordinates of the start position. + var startCoords; + // Last event's position. + var lastPos; + // Whether a swipe is active. + var active = false; + + function getCoordinates(event) { + var touches = event.touches && event.touches.length ? event.touches : [event]; + var e = (event.changedTouches && event.changedTouches[0]) || + (event.originalEvent && event.originalEvent.changedTouches && + event.originalEvent.changedTouches[0]) || + touches[0].originalEvent || touches[0]; + + return { + x: e.clientX, + y: e.clientY + }; + } + + return { + bind: function(element, events) { + element.bind('touchstart mousedown', function(event) { + startCoords = getCoordinates(event); + active = true; + totalX = 0; + totalY = 0; + lastPos = startCoords; + events['start'] && events['start'](startCoords); + }); + + element.bind('touchcancel', function(event) { + active = false; + events['cancel'] && events['cancel'](); + }); + + element.bind('touchmove mousemove', function(event) { + if (!active) return; + + // Android will send a touchcancel if it thinks we're starting to scroll. + // So when the total distance (+ or - or both) exceeds 10px in either direction, + // we either: + // - On totalX > totalY, we send preventDefault() and treat this as a swipe. + // - On totalY > totalX, we let the browser handle it as a scroll. + + if (!startCoords) return; + var coords = getCoordinates(event); + + totalX += Math.abs(coords.x - lastPos.x); + totalY += Math.abs(coords.y - lastPos.y); + + lastPos = coords; + + if (totalX < MOVE_BUFFER_RADIUS && totalY < MOVE_BUFFER_RADIUS) { + return; + } + + // One of totalX or totalY has exceeded the buffer, so decide on swipe vs. scroll. + if (totalY > totalX) { + // Allow native scrolling to take over. + active = false; + return; + } else { + // Prevent the browser from scrolling. + event.preventDefault(); + + events['move'] && events['move'](coords); + } + }); + + element.bind('touchend mouseup', function(event) { + if (!active) return; + active = false; + events['end'] && events['end'](getCoordinates(event)); + }); + } + }; +}]); + +/** + * @ngdoc directive + * @name ngMobile.directive:ngTap + * + * @description + * Specify custom behavior when element is tapped on a touchscreen device. + * A tap is a brief, down-and-up touch without much motion. + * + * @element ANY + * @param {expression} ngClick {@link guide/expression Expression} to evaluate + * upon tap. (Event object is available as `$event`) + * + * @example + <doc:example> + <doc:source> + <button ng-tap="count = count + 1" ng-init="count=0"> + Increment + </button> + count: {{ count }} + </doc:source> + </doc:example> + */ + +ngMobile.config(['$provide', function($provide) { + $provide.decorator('ngClickDirective', ['$delegate', function($delegate) { + // drop the default ngClick directive + $delegate.shift(); + return $delegate; + }]); +}]); + +ngMobile.directive('ngClick', ['$parse', '$timeout', '$rootElement', + function($parse, $timeout, $rootElement) { + var TAP_DURATION = 750; // Shorter than 750ms is a tap, longer is a taphold or drag. + var MOVE_TOLERANCE = 12; // 12px seems to work in most mobile browsers. + var PREVENT_DURATION = 2500; // 2.5 seconds maximum from preventGhostClick call to click + var CLICKBUSTER_THRESHOLD = 25; // 25 pixels in any dimension is the limit for busting clicks. + var lastPreventedTime; + var touchCoordinates; + + + // TAP EVENTS AND GHOST CLICKS + // + // Why tap events? + // Mobile browsers detect a tap, then wait a moment (usually ~300ms) to see if you're + // double-tapping, and then fire a click event. + // + // This delay sucks and makes mobile apps feel unresponsive. + // So we detect touchstart, touchmove, touchcancel and touchend ourselves and determine when + // the user has tapped on something. + // + // What happens when the browser then generates a click event? + // The browser, of course, also detects the tap and fires a click after a delay. This results in + // tapping/clicking twice. So we do "clickbusting" to prevent it. + // + // How does it work? + // We attach global touchstart and click handlers, that run during the capture (early) phase. + // So the sequence for a tap is: + // - global touchstart: Sets an "allowable region" at the point touched. + // - element's touchstart: Starts a touch + // (- touchmove or touchcancel ends the touch, no click follows) + // - element's touchend: Determines if the tap is valid (didn't move too far away, didn't hold + // too long) and fires the user's tap handler. The touchend also calls preventGhostClick(). + // - preventGhostClick() removes the allowable region the global touchstart created. + // - The browser generates a click event. + // - The global click handler catches the click, and checks whether it was in an allowable region. + // - If preventGhostClick was called, the region will have been removed, the click is busted. + // - If the region is still there, the click proceeds normally. Therefore clicks on links and + // other elements without ngTap on them work normally. + // + // This is an ugly, terrible hack! + // Yeah, tell me about it. The alternatives are using the slow click events, or making our users + // deal with the ghost clicks, so I consider this the least of evils. Fortunately Angular + // encapsulates this ugly logic away from the user. + // + // Why not just put click handlers on the element? + // We do that too, just to be sure. The problem is that the tap event might have caused the DOM + // to change, so that the click fires in the same position but something else is there now. So + // the handlers are global and care only about coordinates and not elements. + + // Checks if the coordinates are close enough to be within the region. + function hit(x1, y1, x2, y2) { + return Math.abs(x1 - x2) < CLICKBUSTER_THRESHOLD && Math.abs(y1 - y2) < CLICKBUSTER_THRESHOLD; + } + + // Checks a list of allowable regions against a click location. + // Returns true if the click should be allowed. + // Splices out the allowable region from the list after it has been used. + function checkAllowableRegions(touchCoordinates, x, y) { + for (var i = 0; i < touchCoordinates.length; i += 2) { + if (hit(touchCoordinates[i], touchCoordinates[i+1], x, y)) { + touchCoordinates.splice(i, i + 2); + return true; // allowable region + } + } + return false; // No allowable region; bust it. + } + + // Global click handler that prevents the click if it's in a bustable zone and preventGhostClick + // was called recently. + function onClick(event) { + if (Date.now() - lastPreventedTime > PREVENT_DURATION) { + return; // Too old. + } + + var touches = event.touches && event.touches.length ? event.touches : [event]; + var x = touches[0].clientX; + var y = touches[0].clientY; + // Work around desktop Webkit quirk where clicking a label will fire two clicks (on the label + // and on the input element). Depending on the exact browser, this second click we don't want + // to bust has either (0,0) or negative coordinates. + if (x < 1 && y < 1) { + return; // offscreen + } + + // Look for an allowable region containing this click. + // If we find one, that means it was created by touchstart and not removed by + // preventGhostClick, so we don't bust it. + if (checkAllowableRegions(touchCoordinates, x, y)) { + return; + } + + // If we didn't find an allowable region, bust the click. + event.stopPropagation(); + event.preventDefault(); + } + + + // Global touchstart handler that creates an allowable region for a click event. + // This allowable region can be removed by preventGhostClick if we want to bust it. + function onTouchStart(event) { + var touches = event.touches && event.touches.length ? event.touches : [event]; + var x = touches[0].clientX; + var y = touches[0].clientY; + touchCoordinates.push(x, y); + + $timeout(function() { + // Remove the allowable region. + for (var i = 0; i < touchCoordinates.length; i += 2) { + if (touchCoordinates[i] == x && touchCoordinates[i+1] == y) { + touchCoordinates.splice(i, i + 2); + return; + } + } + }, PREVENT_DURATION, false); + } + + // On the first call, attaches some event handlers. Then whenever it gets called, it creates a + // zone around the touchstart where clicks will get busted. + function preventGhostClick(x, y) { + if (!touchCoordinates) { + $rootElement[0].addEventListener('click', onClick, true); + $rootElement[0].addEventListener('touchstart', onTouchStart, true); + touchCoordinates = []; + } + + lastPreventedTime = Date.now(); + + checkAllowableRegions(touchCoordinates, x, y); + } + + // Actual linking function. + return function(scope, element, attr) { + var clickHandler = $parse(attr.ngClick), + tapping = false, + tapElement, // Used to blur the element after a tap. + startTime, // Used to check if the tap was held too long. + touchStartX, + touchStartY; + + function resetState() { + tapping = false; + } + + element.bind('touchstart', function(event) { + tapping = true; + tapElement = event.target ? event.target : event.srcElement; // IE uses srcElement. + // Hack for Safari, which can target text nodes instead of containers. + if(tapElement.nodeType == 3) { + tapElement = tapElement.parentNode; + } + + startTime = Date.now(); + + var touches = event.touches && event.touches.length ? event.touches : [event]; + var e = touches[0].originalEvent || touches[0]; + touchStartX = e.clientX; + touchStartY = e.clientY; + }); + + element.bind('touchmove', function(event) { + resetState(); + }); + + element.bind('touchcancel', function(event) { + resetState(); + }); + + element.bind('touchend', function(event) { + var diff = Date.now() - startTime; + + var touches = (event.changedTouches && event.changedTouches.length) ? event.changedTouches : + ((event.touches && event.touches.length) ? event.touches : [event]); + var e = touches[0].originalEvent || touches[0]; + var x = e.clientX; + var y = e.clientY; + var dist = Math.sqrt( Math.pow(x - touchStartX, 2) + Math.pow(y - touchStartY, 2) ); + + if (tapping && diff < TAP_DURATION && dist < MOVE_TOLERANCE) { + // Call preventGhostClick so the clickbuster will catch the corresponding click. + preventGhostClick(x, y); + + // Blur the focused element (the button, probably) before firing the callback. + // This doesn't work perfectly on Android Chrome, but seems to work elsewhere. + // I couldn't get anything to work reliably on Android Chrome. + if (tapElement) { + tapElement.blur(); + } + + scope.$apply(function() { + // TODO(braden): This is sending the touchend, not a tap or click. Is that kosher? + clickHandler(scope, {$event: event}); + }); + } + tapping = false; + }); + + // Hack for iOS Safari's benefit. It goes searching for onclick handlers and is liable to click + // something else nearby. + element.onclick = function(event) { }; + + // Fallback click handler. + // Busted clicks don't get this far, and adding this handler allows ng-tap to be used on + // desktop as well, to allow more portable sites. + element.bind('click', function(event) { + scope.$apply(function() { + clickHandler(scope, {$event: event}); + }); + }); + }; +}]); + +/** + * @ngdoc directive + * @name ngMobile.directive:ngSwipeLeft + * + * @description + * Specify custom behavior when an element is swiped to the left on a touchscreen device. + * A leftward swipe is a quick, right-to-left slide of the finger. + * Though ngSwipeLeft is designed for touch-based devices, it will work with a mouse click and drag too. + * + * @element ANY + * @param {expression} ngSwipeLeft {@link guide/expression Expression} to evaluate + * upon left swipe. (Event object is available as `$event`) + * + * @example + <doc:example> + <doc:source> + <div ng-show="!showActions" ng-swipe-left="showActions = true"> + Some list content, like an email in the inbox + </div> + <div ng-show="showActions" ng-swipe-right="showActions = false"> + <button ng-click="reply()">Reply</button> + <button ng-click="delete()">Delete</button> + </div> + </doc:source> + </doc:example> + */ + +/** + * @ngdoc directive + * @name ngMobile.directive:ngSwipeRight + * + * @description + * Specify custom behavior when an element is swiped to the right on a touchscreen device. + * A rightward swipe is a quick, left-to-right slide of the finger. + * Though ngSwipeRight is designed for touch-based devices, it will work with a mouse click and drag too. + * + * @element ANY + * @param {expression} ngSwipeRight {@link guide/expression Expression} to evaluate + * upon right swipe. (Event object is available as `$event`) + * + * @example + <doc:example> + <doc:source> + <div ng-show="!showActions" ng-swipe-left="showActions = true"> + Some list content, like an email in the inbox + </div> + <div ng-show="showActions" ng-swipe-right="showActions = false"> + <button ng-click="reply()">Reply</button> + <button ng-click="delete()">Delete</button> + </div> + </doc:source> + </doc:example> + */ + +function makeSwipeDirective(directiveName, direction) { + ngMobile.directive(directiveName, ['$parse', '$swipe', function($parse, $swipe) { + // The maximum vertical delta for a swipe should be less than 75px. + var MAX_VERTICAL_DISTANCE = 75; + // Vertical distance should not be more than a fraction of the horizontal distance. + var MAX_VERTICAL_RATIO = 0.3; + // At least a 30px lateral motion is necessary for a swipe. + var MIN_HORIZONTAL_DISTANCE = 30; + + return function(scope, element, attr) { + var swipeHandler = $parse(attr[directiveName]); + + var startCoords, valid; + + function validSwipe(coords) { + // Check that it's within the coordinates. + // Absolute vertical distance must be within tolerances. + // Horizontal distance, we take the current X - the starting X. + // This is negative for leftward swipes and positive for rightward swipes. + // After multiplying by the direction (-1 for left, +1 for right), legal swipes + // (ie. same direction as the directive wants) will have a positive delta and + // illegal ones a negative delta. + // Therefore this delta must be positive, and larger than the minimum. + if (!startCoords) return false; + var deltaY = Math.abs(coords.y - startCoords.y); + var deltaX = (coords.x - startCoords.x) * direction; + return valid && // Short circuit for already-invalidated swipes. + deltaY < MAX_VERTICAL_DISTANCE && + deltaX > 0 && + deltaX > MIN_HORIZONTAL_DISTANCE && + deltaY / deltaX < MAX_VERTICAL_RATIO; + } + + $swipe.bind(element, { + 'start': function(coords) { + startCoords = coords; + valid = true; + }, + 'cancel': function() { + valid = false; + }, + 'end': function(coords) { + if (validSwipe(coords)) { + scope.$apply(function() { + swipeHandler(scope); + }); + } + } + }); + }; + }]); +} + +// Left is negative X-coordinate, right is positive. +makeSwipeDirective('ngSwipeLeft', -1); +makeSwipeDirective('ngSwipeRight', 1); + + + +})(window, window.angular); diff --git a/www/lib/angular-carousel/lib/browserTrigger.js b/www/lib/angular-carousel/lib/browserTrigger.js new file mode 100644 index 00000000..dc7a9916 --- /dev/null +++ b/www/lib/angular-carousel/lib/browserTrigger.js @@ -0,0 +1,116 @@ +'use strict'; + +(function() { + var msie = parseInt((/msie (\d+)/.exec(navigator.userAgent.toLowerCase()) || [])[1], 10); + + function indexOf(array, obj) { + if (array.indexOf) return array.indexOf(obj); + + for ( var i = 0; i < array.length; i++) { + if (obj === array[i]) return i; + } + return -1; + } + + + + /** + * Triggers a browser event. Attempts to choose the right event if one is + * not specified. + * + * @param {Object} element Either a wrapped jQuery/jqLite node or a DOMElement + * @param {string} eventType Optional event type. + * @param {Array.<string>=} keys Optional list of pressed keys + * (valid values: 'alt', 'meta', 'shift', 'ctrl') + * @param {number} x Optional x-coordinate for mouse/touch events. + * @param {number} y Optional y-coordinate for mouse/touch events. + */ + window.browserTrigger = function browserTrigger(element, eventType, keys, x, y) { + if (element && !element.nodeName) element = element[0]; + if (!element) return; + + var inputType = (element.type) ? element.type.toLowerCase() : null, + nodeName = element.nodeName.toLowerCase(); + + if (!eventType) { + eventType = { + 'text': 'change', + 'textarea': 'change', + 'hidden': 'change', + 'password': 'change', + 'button': 'click', + 'submit': 'click', + 'reset': 'click', + 'image': 'click', + 'checkbox': 'click', + 'radio': 'click', + 'select-one': 'change', + 'select-multiple': 'change', + '_default_': 'click' + }[inputType || '_default_']; + } + + if (nodeName == 'option') { + element.parentNode.value = element.value; + element = element.parentNode; + eventType = 'change'; + } + + keys = keys || []; + function pressed(key) { + return indexOf(keys, key) !== -1; + } + + if (msie < 9) { + if (inputType == 'radio' || inputType == 'checkbox') { + element.checked = !element.checked; + } + + // WTF!!! Error: Unspecified error. + // Don't know why, but some elements when detached seem to be in inconsistent state and + // calling .fireEvent() on them will result in very unhelpful error (Error: Unspecified error) + // forcing the browser to compute the element position (by reading its CSS) + // puts the element in consistent state. + element.style.posLeft; + + // TODO(vojta): create event objects with pressed keys to get it working on IE<9 + var ret = element.fireEvent('on' + eventType); + if (inputType == 'submit') { + while(element) { + if (element.nodeName.toLowerCase() == 'form') { + element.fireEvent('onsubmit'); + break; + } + element = element.parentNode; + } + } + return ret; + } else { + var evnt = document.createEvent('MouseEvents'), + originalPreventDefault = evnt.preventDefault, + appWindow = element.ownerDocument.defaultView, + fakeProcessDefault = true, + finalProcessDefault, + angular = appWindow.angular || {}; + + // igor: temporary fix for https://bugzilla.mozilla.org/show_bug.cgi?id=684208 + angular['ff-684208-preventDefault'] = false; + evnt.preventDefault = function() { + fakeProcessDefault = false; + return originalPreventDefault.apply(evnt, arguments); + }; + + x = x || 0; + y = y || 0; + evnt.initMouseEvent(eventType, true, true, window, 0, x, y, x, y, pressed('ctrl'), pressed('alt'), + pressed('shift'), pressed('meta'), 0, element); + + element.dispatchEvent(evnt); + finalProcessDefault = !(angular['ff-684208-preventDefault'] || !fakeProcessDefault); + + delete angular['ff-684208-preventDefault']; + + return finalProcessDefault; + } + } +}()); diff --git a/www/lib/angular-carousel/package.json b/www/lib/angular-carousel/package.json new file mode 100644 index 00000000..ea9948b9 --- /dev/null +++ b/www/lib/angular-carousel/package.json @@ -0,0 +1,50 @@ +{ + "name": "angular-carousel", + "description": "Angular Carousel - Mobile friendly touch carousel for AngularJS", + "version": "0.3.12", + "homepage": "http://revolunet.github.com/angular-carousel", + "author": "Julien Bouquillon <julien@revolunet.com>", + "repository": { + "type": "git", + "url": "git://github.com/revolunet/angular-carousel.git" + }, + "engines": { + "node": ">= 0.8" + }, + "licenses": [ + { + "type": "MIT", + "url": "http://revolunet.mit-license.org" + } + ], + "dependencies": {}, + "devDependencies": { + "grunt": "~0.4.2", + "grunt-autoprefixer": "^0.6.5", + "grunt-contrib-concat": "^0.3.0", + "grunt-contrib-connect": "^0.6.0", + "grunt-contrib-copy": "^0.5.0", + "grunt-contrib-cssmin": "^0.8.0", + "grunt-contrib-jshint": "^0.8.0", + "grunt-contrib-sass": "^0.7.2", + "grunt-contrib-uglify": "^0.3.2", + "grunt-contrib-watch": "^0.5.3", + "grunt-conventional-changelog": "~1.1.0", + "grunt-karma": "~0.6.2", + "grunt-ng-annotate": "^0.4.0", + "karma": "~0.10.9", + "karma-chrome-launcher": "~0.1.2", + "karma-firefox-launcher": "~0.1.3", + "karma-html2js-preprocessor": "~0.1.0", + "karma-jasmine": "~0.1.5", + "karma-phantomjs-launcher": "~0.1.2", + "karma-requirejs": "~0.2.1", + "karma-script-launcher": "~0.1.0", + "load-grunt-tasks": "~0.3.0", + "requirejs": "~2.1.11" + }, + "scripts": { + "test": "karma start test/karma.conf.js --single-run --browsers Chrome", + "test-server": "karma start test/karma.conf.js --browsers Chrome" + } +} diff --git a/www/lib/angular-carousel/src/angular-carousel.js b/www/lib/angular-carousel/src/angular-carousel.js new file mode 100644 index 00000000..c8fbd401 --- /dev/null +++ b/www/lib/angular-carousel/src/angular-carousel.js @@ -0,0 +1,12 @@ +/*global angular */ + +/* +Angular touch carousel with CSS GPU accel and slide buffering +http://github.com/revolunet/angular-carousel + +*/ + +angular.module('angular-carousel', [ + 'ngTouch', + 'angular-carousel.shifty' +]); diff --git a/www/lib/angular-carousel/src/css/angular-carousel.scss b/www/lib/angular-carousel/src/css/angular-carousel.scss new file mode 100755 index 00000000..905971f0 --- /dev/null +++ b/www/lib/angular-carousel/src/css/angular-carousel.scss @@ -0,0 +1,74 @@ +input[type=range] { + width:300px; +} + +ul[rn-carousel] { + overflow:hidden; + padding:0; + white-space: nowrap; + position: relative; + perspective:1000px; + -ms-touch-action: pan-y; + touch-action: pan-y; + > li { + color:black; + backface-visibility: hidden; + overflow: visible; + vertical-align: top; + position:absolute; + left:0; + right:0; + white-space: normal; + padding:0; + margin:0; + list-style-type:none; + width:100%; + height:100%; + display:inline-block; + } +} + +/* prevent flickering when moving buffer */ +ul[rn-carousel-buffered] > li { + display:none; +} + +ul[rn-carousel-transition="hexagon"] { + overflow:visible; +} + +/* indicators */ +div.rn-carousel-indicator span { + cursor:pointer; + color: #666; + &.active { + color: white + } +} + +/* prev/next controls */ +.rn-carousel-control { + transition: opacity 0.2s ease-out; + font-size: 2rem; + position: absolute; + top: 40%; + opacity: 0.75; + cursor: pointer; + &:hover { + opacity: 1; + } + + &.rn-carousel-control-prev { + left: 0.5em; + &:before { + content: "<"; + } + } + + &.rn-carousel-control-next { + right: 0.5em; + &:before { + content: ">"; + } + } +} diff --git a/www/lib/angular-carousel/src/directives/rn-carousel-auto-slide.js b/www/lib/angular-carousel/src/directives/rn-carousel-auto-slide.js new file mode 100644 index 00000000..a2e1a5fd --- /dev/null +++ b/www/lib/angular-carousel/src/directives/rn-carousel-auto-slide.js @@ -0,0 +1,31 @@ +angular.module('angular-carousel') + +.directive('rnCarouselAutoSlide', ['$interval', function($interval) { + return { + restrict: 'A', + link: function (scope, element, attrs) { + var stopAutoPlay = function() { + if (scope.autoSlider) { + $interval.cancel(scope.autoSlider); + scope.autoSlider = null; + } + }; + var restartTimer = function() { + scope.autoSlide(); + }; + + scope.$watch('carouselIndex', restartTimer); + + if (attrs.hasOwnProperty('rnCarouselPauseOnHover') && attrs.rnCarouselPauseOnHover !== 'false'){ + element.on('mouseenter', stopAutoPlay); + element.on('mouseleave', restartTimer); + } + + scope.$on('$destroy', function(){ + stopAutoPlay(); + element.off('mouseenter', stopAutoPlay); + element.off('mouseleave', restartTimer); + }); + } + }; +}]); diff --git a/www/lib/angular-carousel/src/directives/rn-carousel-indicators.js b/www/lib/angular-carousel/src/directives/rn-carousel-indicators.js new file mode 100755 index 00000000..f4cc1bd5 --- /dev/null +++ b/www/lib/angular-carousel/src/directives/rn-carousel-indicators.js @@ -0,0 +1,26 @@ +angular.module('angular-carousel') + +.directive('rnCarouselIndicators', ['$parse', function($parse) { + return { + restrict: 'A', + scope: { + slides: '=', + index: '=rnCarouselIndex' + }, + templateUrl: 'carousel-indicators.html', + link: function(scope, iElement, iAttributes) { + var indexModel = $parse(iAttributes.rnCarouselIndex); + scope.goToSlide = function(index) { + indexModel.assign(scope.$parent.$parent, index); + }; + } + }; +}]); + +angular.module('angular-carousel').run(['$templateCache', function($templateCache) { + $templateCache.put('carousel-indicators.html', + '<div class="rn-carousel-indicator">\n' + + '<span ng-repeat="slide in slides" ng-class="{active: $index==index}" ng-click="goToSlide($index)">●</span>' + + '</div>' + ); +}]); diff --git a/www/lib/angular-carousel/src/directives/rn-carousel.js b/www/lib/angular-carousel/src/directives/rn-carousel.js new file mode 100755 index 00000000..9399643c --- /dev/null +++ b/www/lib/angular-carousel/src/directives/rn-carousel.js @@ -0,0 +1,594 @@ +(function() { + "use strict"; + + angular.module('angular-carousel') + + .service('DeviceCapabilities', function() { + + // TODO: merge in a single function + + // detect supported CSS property + function detectTransformProperty() { + var transformProperty = 'transform', + safariPropertyHack = 'webkitTransform'; + if (typeof document.body.style[transformProperty] !== 'undefined') { + + ['webkit', 'moz', 'o', 'ms'].every(function (prefix) { + var e = '-' + prefix + '-transform'; + if (typeof document.body.style[e] !== 'undefined') { + transformProperty = e; + return false; + } + return true; + }); + } else if (typeof document.body.style[safariPropertyHack] !== 'undefined') { + transformProperty = '-webkit-transform'; + } else { + transformProperty = undefined; + } + return transformProperty; + } + + //Detect support of translate3d + function detect3dSupport() { + var el = document.createElement('p'), + has3d, + transforms = { + 'webkitTransform': '-webkit-transform', + 'msTransform': '-ms-transform', + 'transform': 'transform' + }; + // Add it to the body to get the computed style + document.body.insertBefore(el, null); + for (var t in transforms) { + if (el.style[t] !== undefined) { + el.style[t] = 'translate3d(1px,1px,1px)'; + has3d = window.getComputedStyle(el).getPropertyValue(transforms[t]); + } + } + document.body.removeChild(el); + return (has3d !== undefined && has3d.length > 0 && has3d !== "none"); + } + + return { + has3d: detect3dSupport(), + transformProperty: detectTransformProperty() + }; + + }) + + .service('computeCarouselSlideStyle', function(DeviceCapabilities) { + // compute transition transform properties for a given slide and global offset + return function(slideIndex, offset, transitionType) { + var style = { + display: 'inline-block' + }, + opacity, + absoluteLeft = (slideIndex * 100) + offset, + slideTransformValue = DeviceCapabilities.has3d ? 'translate3d(' + absoluteLeft + '%, 0, 0)' : 'translate3d(' + absoluteLeft + '%, 0)', + distance = ((100 - Math.abs(absoluteLeft)) / 100); + + if (!DeviceCapabilities.transformProperty) { + // fallback to default slide if transformProperty is not available + style['margin-left'] = absoluteLeft + '%'; + } else { + if (transitionType == 'fadeAndSlide') { + style[DeviceCapabilities.transformProperty] = slideTransformValue; + opacity = 0; + if (Math.abs(absoluteLeft) < 100) { + opacity = 0.3 + distance * 0.7; + } + style.opacity = opacity; + } else if (transitionType == 'hexagon') { + var transformFrom = 100, + degrees = 0, + maxDegrees = 60 * (distance - 1); + + transformFrom = offset < (slideIndex * -100) ? 100 : 0; + degrees = offset < (slideIndex * -100) ? maxDegrees : -maxDegrees; + style[DeviceCapabilities.transformProperty] = slideTransformValue + ' ' + 'rotateY(' + degrees + 'deg)'; + style[DeviceCapabilities.transformProperty + '-origin'] = transformFrom + '% 50%'; + } else if (transitionType == 'zoom') { + style[DeviceCapabilities.transformProperty] = slideTransformValue; + var scale = 1; + if (Math.abs(absoluteLeft) < 100) { + scale = 1 + ((1 - distance) * 2); + } + style[DeviceCapabilities.transformProperty] += ' scale(' + scale + ')'; + style[DeviceCapabilities.transformProperty + '-origin'] = '50% 50%'; + opacity = 0; + if (Math.abs(absoluteLeft) < 100) { + opacity = 0.3 + distance * 0.7; + } + style.opacity = opacity; + } else { + style[DeviceCapabilities.transformProperty] = slideTransformValue; + } + } + return style; + }; + }) + + .service('createStyleString', function() { + return function(object) { + var styles = []; + angular.forEach(object, function(value, key) { + styles.push(key + ':' + value); + }); + return styles.join(';'); + }; + }) + + .directive('rnCarousel', ['$swipe', '$window', '$document', '$parse', '$compile', '$timeout', '$interval', 'computeCarouselSlideStyle', 'createStyleString', 'Tweenable', + function($swipe, $window, $document, $parse, $compile, $timeout, $interval, computeCarouselSlideStyle, createStyleString, Tweenable) { + // internal ids to allow multiple instances + var carouselId = 0, + // in absolute pixels, at which distance the slide stick to the edge on release + rubberTreshold = 3; + + var requestAnimationFrame = $window.requestAnimationFrame || $window.webkitRequestAnimationFrame || $window.mozRequestAnimationFrame; + + function getItemIndex(collection, target, defaultIndex) { + var result = defaultIndex; + collection.every(function(item, index) { + if (angular.equals(item, target)) { + result = index; + return false; + } + return true; + }); + return result; + } + + return { + restrict: 'A', + scope: true, + compile: function(tElement, tAttributes) { + // use the compile phase to customize the DOM + var firstChild = tElement[0].querySelector('li'), + firstChildAttributes = (firstChild) ? firstChild.attributes : [], + isRepeatBased = false, + isBuffered = false, + repeatItem, + repeatCollection; + + // try to find an ngRepeat expression + // at this point, the attributes are not yet normalized so we need to try various syntax + ['ng-repeat', 'data-ng-repeat', 'ng:repeat', 'x-ng-repeat'].every(function(attr) { + var repeatAttribute = firstChildAttributes[attr]; + if (angular.isDefined(repeatAttribute)) { + // ngRepeat regexp extracted from angular 1.2.7 src + var exprMatch = repeatAttribute.value.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/), + trackProperty = exprMatch[3]; + + repeatItem = exprMatch[1]; + repeatCollection = exprMatch[2]; + + if (repeatItem) { + if (angular.isDefined(tAttributes['rnCarouselBuffered'])) { + // update the current ngRepeat expression and add a slice operator if buffered + isBuffered = true; + repeatAttribute.value = repeatItem + ' in ' + repeatCollection + '|carouselSlice:carouselBufferIndex:carouselBufferSize'; + if (trackProperty) { + repeatAttribute.value += ' track by ' + trackProperty; + } + } + isRepeatBased = true; + return false; + } + } + return true; + }); + + return function(scope, iElement, iAttributes, containerCtrl) { + + carouselId++; + + var defaultOptions = { + transitionType: iAttributes.rnCarouselTransition || 'slide', + transitionEasing: iAttributes.rnCarouselEasing || 'easeTo', + transitionDuration: parseInt(iAttributes.rnCarouselDuration, 10) || 300, + isSequential: true, + autoSlideDuration: 3, + bufferSize: 5, + /* in container % how much we need to drag to trigger the slide change */ + moveTreshold: 0.1, + defaultIndex: 0 + }; + + // TODO + var options = angular.extend({}, defaultOptions); + + var pressed, + startX, + isIndexBound = false, + offset = 0, + destination, + swipeMoved = false, + //animOnIndexChange = true, + currentSlides = [], + elWidth = null, + elX = null, + animateTransitions = true, + intialState = true, + animating = false, + mouseUpBound = false, + locked = false; + + //rn-swipe-disabled =true will only disable swipe events + if(iAttributes.rnSwipeDisabled !== "true") { + $swipe.bind(iElement, { + start: swipeStart, + move: swipeMove, + end: swipeEnd, + cancel: function(event) { + swipeEnd({}, event); + } + }); + } + + function getSlidesDOM() { + return iElement[0].querySelectorAll('ul[rn-carousel] > li'); + } + + function documentMouseUpEvent(event) { + // in case we click outside the carousel, trigger a fake swipeEnd + swipeMoved = true; + swipeEnd({ + x: event.clientX, + y: event.clientY + }, event); + } + + function updateSlidesPosition(offset) { + // manually apply transformation to carousel childrens + // todo : optim : apply only to visible items + var x = scope.carouselBufferIndex * 100 + offset; + angular.forEach(getSlidesDOM(), function(child, index) { + child.style.cssText = createStyleString(computeCarouselSlideStyle(index, x, options.transitionType)); + }); + } + + scope.nextSlide = function(slideOptions) { + var index = scope.carouselIndex + 1; + if (index > currentSlides.length - 1) { + index = 0; + } + if (!locked) { + goToSlide(index, slideOptions); + } + }; + + scope.prevSlide = function(slideOptions) { + var index = scope.carouselIndex - 1; + if (index < 0) { + index = currentSlides.length - 1; + } + goToSlide(index, slideOptions); + }; + + function goToSlide(index, slideOptions) { + //console.log('goToSlide', arguments); + // move a to the given slide index + if (index === undefined) { + index = scope.carouselIndex; + } + + slideOptions = slideOptions || {}; + if (slideOptions.animate === false || options.transitionType === 'none') { + locked = false; + offset = index * -100; + scope.carouselIndex = index; + updateBufferIndex(); + return; + } + + locked = true; + var tweenable = new Tweenable(); + tweenable.tween({ + from: { + 'x': offset + }, + to: { + 'x': index * -100 + }, + duration: options.transitionDuration, + easing: options.transitionEasing, + step: function(state) { + updateSlidesPosition(state.x); + }, + finish: function() { + scope.$apply(function() { + scope.carouselIndex = index; + offset = index * -100; + updateBufferIndex(); + $timeout(function () { + locked = false; + }, 0, false); + }); + } + }); + } + + function getContainerWidth() { + var rect = iElement[0].getBoundingClientRect(); + return rect.width ? rect.width : rect.right - rect.left; + } + + function updateContainerWidth() { + elWidth = getContainerWidth(); + } + + function bindMouseUpEvent() { + if (!mouseUpBound) { + mouseUpBound = true; + $document.bind('mouseup', documentMouseUpEvent); + } + } + + function unbindMouseUpEvent() { + if (mouseUpBound) { + mouseUpBound = false; + $document.unbind('mouseup', documentMouseUpEvent); + } + } + + function swipeStart(coords, event) { + // console.log('swipeStart', coords, event); + if (locked || currentSlides.length <= 1) { + return; + } + updateContainerWidth(); + elX = iElement[0].querySelector('li').getBoundingClientRect().left; + pressed = true; + startX = coords.x; + return false; + } + + function swipeMove(coords, event) { + //console.log('swipeMove', coords, event); + var x, delta; + bindMouseUpEvent(); + if (pressed) { + x = coords.x; + delta = startX - x; + if (delta > 2 || delta < -2) { + swipeMoved = true; + var moveOffset = offset + (-delta * 100 / elWidth); + updateSlidesPosition(moveOffset); + } + } + return false; + } + + var init = true; + scope.carouselIndex = 0; + + if (!isRepeatBased) { + // fake array when no ng-repeat + currentSlides = []; + angular.forEach(getSlidesDOM(), function(node, index) { + currentSlides.push({id: index}); + }); + } + + if (iAttributes.rnCarouselControls!==undefined) { + // dont use a directive for this + var nextSlideIndexCompareValue = isRepeatBased ? repeatCollection.replace('::', '') + '.length - 1' : currentSlides.length - 1; + var tpl = '<div class="rn-carousel-controls">\n' + + ' <span class="rn-carousel-control rn-carousel-control-prev" ng-click="prevSlide()" ng-if="carouselIndex > 0"></span>\n' + + ' <span class="rn-carousel-control rn-carousel-control-next" ng-click="nextSlide()" ng-if="carouselIndex < ' + nextSlideIndexCompareValue + '"></span>\n' + + '</div>'; + iElement.parent().append($compile(angular.element(tpl))(scope)); + } + + if (iAttributes.rnCarouselAutoSlide!==undefined) { + var duration = parseInt(iAttributes.rnCarouselAutoSlide, 10) || options.autoSlideDuration; + scope.autoSlide = function() { + if (scope.autoSlider) { + $interval.cancel(scope.autoSlider); + scope.autoSlider = null; + } + scope.autoSlider = $interval(function() { + if (!locked && !pressed) { + scope.nextSlide(); + } + }, duration * 1000); + }; + } + + if (iAttributes.rnCarouselDefaultIndex) { + var defaultIndexModel = $parse(iAttributes.rnCarouselDefaultIndex); + options.defaultIndex = defaultIndexModel(scope.$parent) || 0; + } + + if (iAttributes.rnCarouselIndex) { + var updateParentIndex = function(value) { + indexModel.assign(scope.$parent, value); + }; + var indexModel = $parse(iAttributes.rnCarouselIndex); + if (angular.isFunction(indexModel.assign)) { + /* check if this property is assignable then watch it */ + scope.$watch('carouselIndex', function(newValue) { + updateParentIndex(newValue); + }); + scope.$parent.$watch(indexModel, function(newValue, oldValue) { + + if (newValue !== undefined && newValue !== null) { + if (currentSlides && currentSlides.length > 0 && newValue >= currentSlides.length) { + newValue = currentSlides.length - 1; + updateParentIndex(newValue); + } else if (currentSlides && newValue < 0) { + newValue = 0; + updateParentIndex(newValue); + } + if (!locked) { + goToSlide(newValue, { + animate: !init + }); + } + init = false; + } + }); + isIndexBound = true; + + if (options.defaultIndex) { + goToSlide(options.defaultIndex, { + animate: !init + }); + } + } else if (!isNaN(iAttributes.rnCarouselIndex)) { + /* if user just set an initial number, set it */ + goToSlide(parseInt(iAttributes.rnCarouselIndex, 10), { + animate: false + }); + } + } else { + goToSlide(options.defaultIndex, { + animate: !init + }); + init = false; + } + + if (iAttributes.rnCarouselLocked) { + scope.$watch(iAttributes.rnCarouselLocked, function(newValue, oldValue) { + // only bind swipe when it's not switched off + if(newValue === true) { + locked = true; + } else { + locked = false; + } + }); + } + + if (isRepeatBased) { + // use rn-carousel-deep-watch to fight the Angular $watchCollection weakness : https://github.com/angular/angular.js/issues/2621 + // optional because it have some performance impacts (deep watch) + var deepWatch = (iAttributes.rnCarouselDeepWatch!==undefined); + + scope[deepWatch?'$watch':'$watchCollection'](repeatCollection, function(newValue, oldValue) { + //console.log('repeatCollection', currentSlides); + currentSlides = newValue; + // if deepWatch ON ,manually compare objects to guess the new position + if (deepWatch && angular.isArray(newValue)) { + var activeElement = oldValue[scope.carouselIndex]; + var newIndex = getItemIndex(newValue, activeElement, scope.carouselIndex); + goToSlide(newIndex, {animate: false}); + } else { + goToSlide(scope.carouselIndex, {animate: false}); + } + }, true); + } + + function swipeEnd(coords, event, forceAnimation) { + // console.log('swipeEnd', 'scope.carouselIndex', scope.carouselIndex); + // Prevent clicks on buttons inside slider to trigger "swipeEnd" event on touchend/mouseup + // console.log(iAttributes.rnCarouselOnInfiniteScroll); + if (event && !swipeMoved) { + return; + } + unbindMouseUpEvent(); + pressed = false; + swipeMoved = false; + destination = startX - coords.x; + if (destination===0) { + return; + } + if (locked) { + return; + } + offset += (-destination * 100 / elWidth); + if (options.isSequential) { + var minMove = options.moveTreshold * elWidth, + absMove = -destination, + slidesMove = -Math[absMove >= 0 ? 'ceil' : 'floor'](absMove / elWidth), + shouldMove = Math.abs(absMove) > minMove; + + if (currentSlides && (slidesMove + scope.carouselIndex) >= currentSlides.length) { + slidesMove = currentSlides.length - 1 - scope.carouselIndex; + } + if ((slidesMove + scope.carouselIndex) < 0) { + slidesMove = -scope.carouselIndex; + } + var moveOffset = shouldMove ? slidesMove : 0; + + destination = (scope.carouselIndex + moveOffset); + + goToSlide(destination); + if(iAttributes.rnCarouselOnInfiniteScrollRight!==undefined && slidesMove === 0 && scope.carouselIndex !== 0) { + $parse(iAttributes.rnCarouselOnInfiniteScrollRight)(scope) + goToSlide(0); + } + if(iAttributes.rnCarouselOnInfiniteScrollLeft!==undefined && slidesMove === 0 && scope.carouselIndex === 0 && moveOffset === 0) { + $parse(iAttributes.rnCarouselOnInfiniteScrollLeft)(scope) + goToSlide(currentSlides.length); + } + + } else { + scope.$apply(function() { + scope.carouselIndex = parseInt(-offset / 100, 10); + updateBufferIndex(); + }); + + } + + } + + scope.$on('$destroy', function() { + unbindMouseUpEvent(); + }); + + scope.carouselBufferIndex = 0; + scope.carouselBufferSize = options.bufferSize; + + function updateBufferIndex() { + // update and cap te buffer index + var bufferIndex = 0; + var bufferEdgeSize = (scope.carouselBufferSize - 1) / 2; + if (isBuffered) { + if (scope.carouselIndex <= bufferEdgeSize) { + // first buffer part + bufferIndex = 0; + } else if (currentSlides && currentSlides.length < scope.carouselBufferSize) { + // smaller than buffer + bufferIndex = 0; + } else if (currentSlides && scope.carouselIndex > currentSlides.length - scope.carouselBufferSize) { + // last buffer part + bufferIndex = currentSlides.length - scope.carouselBufferSize; + } else { + // compute buffer start + bufferIndex = scope.carouselIndex - bufferEdgeSize; + } + + scope.carouselBufferIndex = bufferIndex; + $timeout(function() { + updateSlidesPosition(offset); + }, 0, false); + } else { + $timeout(function() { + updateSlidesPosition(offset); + }, 0, false); + } + } + + function onOrientationChange() { + updateContainerWidth(); + goToSlide(); + } + + // handle orientation change + var winEl = angular.element($window); + winEl.bind('orientationchange', onOrientationChange); + winEl.bind('resize', onOrientationChange); + + scope.$on('$destroy', function() { + unbindMouseUpEvent(); + winEl.unbind('orientationchange', onOrientationChange); + winEl.unbind('resize', onOrientationChange); + }); + }; + } + }; + } + ]); +})(); diff --git a/www/lib/angular-carousel/src/directives/shifty.js b/www/lib/angular-carousel/src/directives/shifty.js new file mode 100644 index 00000000..d59f990b --- /dev/null +++ b/www/lib/angular-carousel/src/directives/shifty.js @@ -0,0 +1,1405 @@ + + +angular.module('angular-carousel.shifty', []) + +.factory('Tweenable', function() { + + /*! shifty - v1.3.4 - 2014-10-29 - http://jeremyckahn.github.io/shifty */ + ;(function (root) { + + /*! + * Shifty Core + * By Jeremy Kahn - jeremyckahn@gmail.com + */ + + var Tweenable = (function () { + + 'use strict'; + + // Aliases that get defined later in this function + var formula; + + // CONSTANTS + var DEFAULT_SCHEDULE_FUNCTION; + var DEFAULT_EASING = 'linear'; + var DEFAULT_DURATION = 500; + var UPDATE_TIME = 1000 / 60; + + var _now = Date.now + ? Date.now + : function () {return +new Date();}; + + var now = typeof SHIFTY_DEBUG_NOW !== 'undefined' ? SHIFTY_DEBUG_NOW : _now; + + if (typeof window !== 'undefined') { + // requestAnimationFrame() shim by Paul Irish (modified for Shifty) + // http://paulirish.com/2011/requestanimationframe-for-smart-animating/ + DEFAULT_SCHEDULE_FUNCTION = window.requestAnimationFrame + || window.webkitRequestAnimationFrame + || window.oRequestAnimationFrame + || window.msRequestAnimationFrame + || (window.mozCancelRequestAnimationFrame + && window.mozRequestAnimationFrame) + || setTimeout; + } else { + DEFAULT_SCHEDULE_FUNCTION = setTimeout; + } + + function noop () { + // NOOP! + } + + /*! + * Handy shortcut for doing a for-in loop. This is not a "normal" each + * function, it is optimized for Shifty. The iterator function only receives + * the property name, not the value. + * @param {Object} obj + * @param {Function(string)} fn + */ + function each (obj, fn) { + var key; + for (key in obj) { + if (Object.hasOwnProperty.call(obj, key)) { + fn(key); + } + } + } + + /*! + * Perform a shallow copy of Object properties. + * @param {Object} targetObject The object to copy into + * @param {Object} srcObject The object to copy from + * @return {Object} A reference to the augmented `targetObj` Object + */ + function shallowCopy (targetObj, srcObj) { + each(srcObj, function (prop) { + targetObj[prop] = srcObj[prop]; + }); + + return targetObj; + } + + /*! + * Copies each property from src onto target, but only if the property to + * copy to target is undefined. + * @param {Object} target Missing properties in this Object are filled in + * @param {Object} src + */ + function defaults (target, src) { + each(src, function (prop) { + if (typeof target[prop] === 'undefined') { + target[prop] = src[prop]; + } + }); + } + + /*! + * Calculates the interpolated tween values of an Object for a given + * timestamp. + * @param {Number} forPosition The position to compute the state for. + * @param {Object} currentState Current state properties. + * @param {Object} originalState: The original state properties the Object is + * tweening from. + * @param {Object} targetState: The destination state properties the Object + * is tweening to. + * @param {number} duration: The length of the tween in milliseconds. + * @param {number} timestamp: The UNIX epoch time at which the tween began. + * @param {Object} easing: This Object's keys must correspond to the keys in + * targetState. + */ + function tweenProps (forPosition, currentState, originalState, targetState, + duration, timestamp, easing) { + var normalizedPosition = (forPosition - timestamp) / duration; + + var prop; + for (prop in currentState) { + if (currentState.hasOwnProperty(prop)) { + currentState[prop] = tweenProp(originalState[prop], + targetState[prop], formula[easing[prop]], normalizedPosition); + } + } + + return currentState; + } + + /*! + * Tweens a single property. + * @param {number} start The value that the tween started from. + * @param {number} end The value that the tween should end at. + * @param {Function} easingFunc The easing curve to apply to the tween. + * @param {number} position The normalized position (between 0.0 and 1.0) to + * calculate the midpoint of 'start' and 'end' against. + * @return {number} The tweened value. + */ + function tweenProp (start, end, easingFunc, position) { + return start + (end - start) * easingFunc(position); + } + + /*! + * Applies a filter to Tweenable instance. + * @param {Tweenable} tweenable The `Tweenable` instance to call the filter + * upon. + * @param {String} filterName The name of the filter to apply. + */ + function applyFilter (tweenable, filterName) { + var filters = Tweenable.prototype.filter; + var args = tweenable._filterArgs; + + each(filters, function (name) { + if (typeof filters[name][filterName] !== 'undefined') { + filters[name][filterName].apply(tweenable, args); + } + }); + } + + var timeoutHandler_endTime; + var timeoutHandler_currentTime; + var timeoutHandler_isEnded; + var timeoutHandler_offset; + /*! + * Handles the update logic for one step of a tween. + * @param {Tweenable} tweenable + * @param {number} timestamp + * @param {number} duration + * @param {Object} currentState + * @param {Object} originalState + * @param {Object} targetState + * @param {Object} easing + * @param {Function(Object, *, number)} step + * @param {Function(Function,number)}} schedule + */ + function timeoutHandler (tweenable, timestamp, duration, currentState, + originalState, targetState, easing, step, schedule) { + timeoutHandler_endTime = timestamp + duration; + timeoutHandler_currentTime = Math.min(now(), timeoutHandler_endTime); + timeoutHandler_isEnded = + timeoutHandler_currentTime >= timeoutHandler_endTime; + + timeoutHandler_offset = duration - ( + timeoutHandler_endTime - timeoutHandler_currentTime); + + if (tweenable.isPlaying() && !timeoutHandler_isEnded) { + tweenable._scheduleId = schedule(tweenable._timeoutHandler, UPDATE_TIME); + + applyFilter(tweenable, 'beforeTween'); + tweenProps(timeoutHandler_currentTime, currentState, originalState, + targetState, duration, timestamp, easing); + applyFilter(tweenable, 'afterTween'); + + step(currentState, tweenable._attachment, timeoutHandler_offset); + } else if (timeoutHandler_isEnded) { + step(targetState, tweenable._attachment, timeoutHandler_offset); + tweenable.stop(true); + } + } + + + /*! + * Creates a usable easing Object from either a string or another easing + * Object. If `easing` is an Object, then this function clones it and fills + * in the missing properties with "linear". + * @param {Object} fromTweenParams + * @param {Object|string} easing + */ + function composeEasingObject (fromTweenParams, easing) { + var composedEasing = {}; + + if (typeof easing === 'string') { + each(fromTweenParams, function (prop) { + composedEasing[prop] = easing; + }); + } else { + each(fromTweenParams, function (prop) { + if (!composedEasing[prop]) { + composedEasing[prop] = easing[prop] || DEFAULT_EASING; + } + }); + } + + return composedEasing; + } + + /** + * Tweenable constructor. + * @param {Object=} opt_initialState The values that the initial tween should start at if a "from" object is not provided to Tweenable#tween. + * @param {Object=} opt_config See Tweenable.prototype.setConfig() + * @constructor + */ + function Tweenable (opt_initialState, opt_config) { + this._currentState = opt_initialState || {}; + this._configured = false; + this._scheduleFunction = DEFAULT_SCHEDULE_FUNCTION; + + // To prevent unnecessary calls to setConfig do not set default configuration here. + // Only set default configuration immediately before tweening if none has been set. + if (typeof opt_config !== 'undefined') { + this.setConfig(opt_config); + } + } + + /** + * Configure and start a tween. + * @param {Object=} opt_config See Tweenable.prototype.setConfig() + * @return {Tweenable} + */ + Tweenable.prototype.tween = function (opt_config) { + if (this._isTweening) { + return this; + } + + // Only set default config if no configuration has been set previously and none is provided now. + if (opt_config !== undefined || !this._configured) { + this.setConfig(opt_config); + } + + this._timestamp = now(); + this._start(this.get(), this._attachment); + return this.resume(); + }; + + /** + * Sets the tween configuration. `config` may have the following options: + * + * - __from__ (_Object=_): Starting position. If omitted, the current state is used. + * - __to__ (_Object=_): Ending position. + * - __duration__ (_number=_): How many milliseconds to animate for. + * - __start__ (_Function(Object)_): Function to execute when the tween begins. Receives the state of the tween as the first parameter. Attachment is the second parameter. + * - __step__ (_Function(Object, *, number)_): Function to execute on every tick. Receives the state of the tween as the first parameter. Attachment is the second parameter, and the time elapsed since the start of the tween is the third parameter. This function is not called on the final step of the animation, but `finish` is. + * - __finish__ (_Function(Object, *)_): Function to execute upon tween completion. Receives the state of the tween as the first parameter. Attachment is the second parameter. + * - __easing__ (_Object|string=_): Easing curve name(s) to use for the tween. + * - __attachment__ (_Object|string|any=_): Value that is attached to this instance and passed on to the step/start/finish methods. + * @param {Object} config + * @return {Tweenable} + */ + Tweenable.prototype.setConfig = function (config) { + config = config || {}; + this._configured = true; + + // Attach something to this Tweenable instance (e.g.: a DOM element, an object, a string, etc.); + this._attachment = config.attachment; + + // Init the internal state + this._pausedAtTime = null; + this._scheduleId = null; + this._start = config.start || noop; + this._step = config.step || noop; + this._finish = config.finish || noop; + this._duration = config.duration || DEFAULT_DURATION; + this._currentState = config.from || this.get(); + this._originalState = this.get(); + this._targetState = config.to || this.get(); + + // Aliases used below + var currentState = this._currentState; + var targetState = this._targetState; + + // Ensure that there is always something to tween to. + defaults(targetState, currentState); + + this._easing = composeEasingObject( + currentState, config.easing || DEFAULT_EASING); + + this._filterArgs = + [currentState, this._originalState, targetState, this._easing]; + + applyFilter(this, 'tweenCreated'); + return this; + }; + + /** + * Gets the current state. + * @return {Object} + */ + Tweenable.prototype.get = function () { + return shallowCopy({}, this._currentState); + }; + + /** + * Sets the current state. + * @param {Object} state + */ + Tweenable.prototype.set = function (state) { + this._currentState = state; + }; + + /** + * Pauses a tween. Paused tweens can be resumed from the point at which they were paused. This is different than [`stop()`](#stop), as that method causes a tween to start over when it is resumed. + * @return {Tweenable} + */ + Tweenable.prototype.pause = function () { + this._pausedAtTime = now(); + this._isPaused = true; + return this; + }; + + /** + * Resumes a paused tween. + * @return {Tweenable} + */ + Tweenable.prototype.resume = function () { + if (this._isPaused) { + this._timestamp += now() - this._pausedAtTime; + } + + this._isPaused = false; + this._isTweening = true; + + var self = this; + this._timeoutHandler = function () { + timeoutHandler(self, self._timestamp, self._duration, self._currentState, + self._originalState, self._targetState, self._easing, self._step, + self._scheduleFunction); + }; + + this._timeoutHandler(); + + return this; + }; + + /** + * Move the state of the animation to a specific point in the tween's timeline. + * If the animation is not running, this will cause the `step` handlers to be + * called. + * @param {millisecond} millisecond The millisecond of the animation to seek to. + * @return {Tweenable} + */ + Tweenable.prototype.seek = function (millisecond) { + this._timestamp = now() - millisecond; + + if (!this.isPlaying()) { + this._isTweening = true; + this._isPaused = false; + + // If the animation is not running, call timeoutHandler to make sure that + // any step handlers are run. + timeoutHandler(this, this._timestamp, this._duration, this._currentState, + this._originalState, this._targetState, this._easing, this._step, + this._scheduleFunction); + + this._timeoutHandler(); + this.pause(); + } + + return this; + }; + + /** + * Stops and cancels a tween. + * @param {boolean=} gotoEnd If false or omitted, the tween just stops at its current state, and the "finish" handler is not invoked. If true, the tweened object's values are instantly set to the target values, and "finish" is invoked. + * @return {Tweenable} + */ + Tweenable.prototype.stop = function (gotoEnd) { + this._isTweening = false; + this._isPaused = false; + this._timeoutHandler = noop; + + (root.cancelAnimationFrame || + root.webkitCancelAnimationFrame || + root.oCancelAnimationFrame || + root.msCancelAnimationFrame || + root.mozCancelRequestAnimationFrame || + root.clearTimeout)(this._scheduleId); + + if (gotoEnd) { + shallowCopy(this._currentState, this._targetState); + applyFilter(this, 'afterTweenEnd'); + this._finish.call(this, this._currentState, this._attachment); + } + + return this; + }; + + /** + * Returns whether or not a tween is running. + * @return {boolean} + */ + Tweenable.prototype.isPlaying = function () { + return this._isTweening && !this._isPaused; + }; + + /** + * Sets a custom schedule function. + * + * If a custom function is not set the default one is used [`requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame) if available, otherwise [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/Window.setTimeout)). + * + * @param {Function(Function,number)} scheduleFunction The function to be called to schedule the next frame to be rendered + */ + Tweenable.prototype.setScheduleFunction = function (scheduleFunction) { + this._scheduleFunction = scheduleFunction; + }; + + /** + * `delete`s all "own" properties. Call this when the `Tweenable` instance is no longer needed to free memory. + */ + Tweenable.prototype.dispose = function () { + var prop; + for (prop in this) { + if (this.hasOwnProperty(prop)) { + delete this[prop]; + } + } + }; + + /*! + * Filters are used for transforming the properties of a tween at various + * points in a Tweenable's life cycle. See the README for more info on this. + */ + Tweenable.prototype.filter = {}; + + /*! + * This object contains all of the tweens available to Shifty. It is extendible - simply attach properties to the Tweenable.prototype.formula Object following the same format at linear. + * + * `pos` should be a normalized `number` (between 0 and 1). + */ + Tweenable.prototype.formula = { + linear: function (pos) { + return pos; + } + }; + + formula = Tweenable.prototype.formula; + + shallowCopy(Tweenable, { + 'now': now + ,'each': each + ,'tweenProps': tweenProps + ,'tweenProp': tweenProp + ,'applyFilter': applyFilter + ,'shallowCopy': shallowCopy + ,'defaults': defaults + ,'composeEasingObject': composeEasingObject + }); + + root.Tweenable = Tweenable; + return Tweenable; + + } ()); + + /*! + * All equations are adapted from Thomas Fuchs' [Scripty2](https://github.com/madrobby/scripty2/blob/master/src/effects/transitions/penner.js). + * + * Based on Easing Equations (c) 2003 [Robert Penner](http://www.robertpenner.com/), all rights reserved. This work is [subject to terms](http://www.robertpenner.com/easing_terms_of_use.html). + */ + + /*! + * TERMS OF USE - EASING EQUATIONS + * Open source under the BSD License. + * Easing Equations (c) 2003 Robert Penner, all rights reserved. + */ + + ;(function () { + + Tweenable.shallowCopy(Tweenable.prototype.formula, { + easeInQuad: function (pos) { + return Math.pow(pos, 2); + }, + + easeOutQuad: function (pos) { + return -(Math.pow((pos - 1), 2) - 1); + }, + + easeInOutQuad: function (pos) { + if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,2);} + return -0.5 * ((pos -= 2) * pos - 2); + }, + + easeInCubic: function (pos) { + return Math.pow(pos, 3); + }, + + easeOutCubic: function (pos) { + return (Math.pow((pos - 1), 3) + 1); + }, + + easeInOutCubic: function (pos) { + if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,3);} + return 0.5 * (Math.pow((pos - 2),3) + 2); + }, + + easeInQuart: function (pos) { + return Math.pow(pos, 4); + }, + + easeOutQuart: function (pos) { + return -(Math.pow((pos - 1), 4) - 1); + }, + + easeInOutQuart: function (pos) { + if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,4);} + return -0.5 * ((pos -= 2) * Math.pow(pos,3) - 2); + }, + + easeInQuint: function (pos) { + return Math.pow(pos, 5); + }, + + easeOutQuint: function (pos) { + return (Math.pow((pos - 1), 5) + 1); + }, + + easeInOutQuint: function (pos) { + if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,5);} + return 0.5 * (Math.pow((pos - 2),5) + 2); + }, + + easeInSine: function (pos) { + return -Math.cos(pos * (Math.PI / 2)) + 1; + }, + + easeOutSine: function (pos) { + return Math.sin(pos * (Math.PI / 2)); + }, + + easeInOutSine: function (pos) { + return (-0.5 * (Math.cos(Math.PI * pos) - 1)); + }, + + easeInExpo: function (pos) { + return (pos === 0) ? 0 : Math.pow(2, 10 * (pos - 1)); + }, + + easeOutExpo: function (pos) { + return (pos === 1) ? 1 : -Math.pow(2, -10 * pos) + 1; + }, + + easeInOutExpo: function (pos) { + if (pos === 0) {return 0;} + if (pos === 1) {return 1;} + if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(2,10 * (pos - 1));} + return 0.5 * (-Math.pow(2, -10 * --pos) + 2); + }, + + easeInCirc: function (pos) { + return -(Math.sqrt(1 - (pos * pos)) - 1); + }, + + easeOutCirc: function (pos) { + return Math.sqrt(1 - Math.pow((pos - 1), 2)); + }, + + easeInOutCirc: function (pos) { + if ((pos /= 0.5) < 1) {return -0.5 * (Math.sqrt(1 - pos * pos) - 1);} + return 0.5 * (Math.sqrt(1 - (pos -= 2) * pos) + 1); + }, + + easeOutBounce: function (pos) { + if ((pos) < (1 / 2.75)) { + return (7.5625 * pos * pos); + } else if (pos < (2 / 2.75)) { + return (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75); + } else if (pos < (2.5 / 2.75)) { + return (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375); + } else { + return (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375); + } + }, + + easeInBack: function (pos) { + var s = 1.70158; + return (pos) * pos * ((s + 1) * pos - s); + }, + + easeOutBack: function (pos) { + var s = 1.70158; + return (pos = pos - 1) * pos * ((s + 1) * pos + s) + 1; + }, + + easeInOutBack: function (pos) { + var s = 1.70158; + if ((pos /= 0.5) < 1) {return 0.5 * (pos * pos * (((s *= (1.525)) + 1) * pos - s));} + return 0.5 * ((pos -= 2) * pos * (((s *= (1.525)) + 1) * pos + s) + 2); + }, + + elastic: function (pos) { + return -1 * Math.pow(4,-8 * pos) * Math.sin((pos * 6 - 1) * (2 * Math.PI) / 2) + 1; + }, + + swingFromTo: function (pos) { + var s = 1.70158; + return ((pos /= 0.5) < 1) ? 0.5 * (pos * pos * (((s *= (1.525)) + 1) * pos - s)) : + 0.5 * ((pos -= 2) * pos * (((s *= (1.525)) + 1) * pos + s) + 2); + }, + + swingFrom: function (pos) { + var s = 1.70158; + return pos * pos * ((s + 1) * pos - s); + }, + + swingTo: function (pos) { + var s = 1.70158; + return (pos -= 1) * pos * ((s + 1) * pos + s) + 1; + }, + + bounce: function (pos) { + if (pos < (1 / 2.75)) { + return (7.5625 * pos * pos); + } else if (pos < (2 / 2.75)) { + return (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75); + } else if (pos < (2.5 / 2.75)) { + return (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375); + } else { + return (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375); + } + }, + + bouncePast: function (pos) { + if (pos < (1 / 2.75)) { + return (7.5625 * pos * pos); + } else if (pos < (2 / 2.75)) { + return 2 - (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75); + } else if (pos < (2.5 / 2.75)) { + return 2 - (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375); + } else { + return 2 - (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375); + } + }, + + easeFromTo: function (pos) { + if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,4);} + return -0.5 * ((pos -= 2) * Math.pow(pos,3) - 2); + }, + + easeFrom: function (pos) { + return Math.pow(pos,4); + }, + + easeTo: function (pos) { + return Math.pow(pos,0.25); + } + }); + + }()); + + /*! + * The Bezier magic in this file is adapted/copied almost wholesale from + * [Scripty2](https://github.com/madrobby/scripty2/blob/master/src/effects/transitions/cubic-bezier.js), + * which was adapted from Apple code (which probably came from + * [here](http://opensource.apple.com/source/WebCore/WebCore-955.66/platform/graphics/UnitBezier.h)). + * Special thanks to Apple and Thomas Fuchs for much of this code. + */ + + /*! + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder(s) nor the names of any + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + ;(function () { + // port of webkit cubic bezier handling by http://www.netzgesta.de/dev/ + function cubicBezierAtTime(t,p1x,p1y,p2x,p2y,duration) { + var ax = 0,bx = 0,cx = 0,ay = 0,by = 0,cy = 0; + function sampleCurveX(t) {return ((ax * t + bx) * t + cx) * t;} + function sampleCurveY(t) {return ((ay * t + by) * t + cy) * t;} + function sampleCurveDerivativeX(t) {return (3.0 * ax * t + 2.0 * bx) * t + cx;} + function solveEpsilon(duration) {return 1.0 / (200.0 * duration);} + function solve(x,epsilon) {return sampleCurveY(solveCurveX(x,epsilon));} + function fabs(n) {if (n >= 0) {return n;}else {return 0 - n;}} + function solveCurveX(x,epsilon) { + var t0,t1,t2,x2,d2,i; + for (t2 = x, i = 0; i < 8; i++) {x2 = sampleCurveX(t2) - x; if (fabs(x2) < epsilon) {return t2;} d2 = sampleCurveDerivativeX(t2); if (fabs(d2) < 1e-6) {break;} t2 = t2 - x2 / d2;} + t0 = 0.0; t1 = 1.0; t2 = x; if (t2 < t0) {return t0;} if (t2 > t1) {return t1;} + while (t0 < t1) {x2 = sampleCurveX(t2); if (fabs(x2 - x) < epsilon) {return t2;} if (x > x2) {t0 = t2;}else {t1 = t2;} t2 = (t1 - t0) * 0.5 + t0;} + return t2; // Failure. + } + cx = 3.0 * p1x; bx = 3.0 * (p2x - p1x) - cx; ax = 1.0 - cx - bx; cy = 3.0 * p1y; by = 3.0 * (p2y - p1y) - cy; ay = 1.0 - cy - by; + return solve(t, solveEpsilon(duration)); + } + /*! + * getCubicBezierTransition(x1, y1, x2, y2) -> Function + * + * Generates a transition easing function that is compatible + * with WebKit's CSS transitions `-webkit-transition-timing-function` + * CSS property. + * + * The W3C has more information about + * <a href="http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag"> + * CSS3 transition timing functions</a>. + * + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @return {function} + */ + function getCubicBezierTransition (x1, y1, x2, y2) { + return function (pos) { + return cubicBezierAtTime(pos,x1,y1,x2,y2,1); + }; + } + // End ported code + + /** + * Creates a Bezier easing function and attaches it to `Tweenable.prototype.formula`. This function gives you total control over the easing curve. Matthew Lein's [Ceaser](http://matthewlein.com/ceaser/) is a useful tool for visualizing the curves you can make with this function. + * + * @param {string} name The name of the easing curve. Overwrites the old easing function on Tweenable.prototype.formula if it exists. + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @return {function} The easing function that was attached to Tweenable.prototype.formula. + */ + Tweenable.setBezierFunction = function (name, x1, y1, x2, y2) { + var cubicBezierTransition = getCubicBezierTransition(x1, y1, x2, y2); + cubicBezierTransition.x1 = x1; + cubicBezierTransition.y1 = y1; + cubicBezierTransition.x2 = x2; + cubicBezierTransition.y2 = y2; + + return Tweenable.prototype.formula[name] = cubicBezierTransition; + }; + + + /** + * `delete`s an easing function from `Tweenable.prototype.formula`. Be careful with this method, as it `delete`s whatever easing formula matches `name` (which means you can delete default Shifty easing functions). + * + * @param {string} name The name of the easing function to delete. + * @return {function} + */ + Tweenable.unsetBezierFunction = function (name) { + delete Tweenable.prototype.formula[name]; + }; + + })(); + + ;(function () { + + function getInterpolatedValues ( + from, current, targetState, position, easing) { + return Tweenable.tweenProps( + position, current, from, targetState, 1, 0, easing); + } + + // Fake a Tweenable and patch some internals. This approach allows us to + // skip uneccessary processing and object recreation, cutting down on garbage + // collection pauses. + var mockTweenable = new Tweenable(); + mockTweenable._filterArgs = []; + + /** + * Compute the midpoint of two Objects. This method effectively calculates a specific frame of animation that [Tweenable#tween](shifty.core.js.html#tween) does many times over the course of a tween. + * + * Example: + * + * var interpolatedValues = Tweenable.interpolate({ + * width: '100px', + * opacity: 0, + * color: '#fff' + * }, { + * width: '200px', + * opacity: 1, + * color: '#000' + * }, 0.5); + * + * console.log(interpolatedValues); + * // {opacity: 0.5, width: "150px", color: "rgb(127,127,127)"} + * + * @param {Object} from The starting values to tween from. + * @param {Object} targetState The ending values to tween to. + * @param {number} position The normalized position value (between 0.0 and 1.0) to interpolate the values between `from` and `to` for. `from` represents 0 and `to` represents `1`. + * @param {string|Object} easing The easing curve(s) to calculate the midpoint against. You can reference any easing function attached to `Tweenable.prototype.formula`. If omitted, this defaults to "linear". + * @return {Object} + */ + Tweenable.interpolate = function (from, targetState, position, easing) { + var current = Tweenable.shallowCopy({}, from); + var easingObject = Tweenable.composeEasingObject( + from, easing || 'linear'); + + mockTweenable.set({}); + + // Alias and reuse the _filterArgs array instead of recreating it. + var filterArgs = mockTweenable._filterArgs; + filterArgs.length = 0; + filterArgs[0] = current; + filterArgs[1] = from; + filterArgs[2] = targetState; + filterArgs[3] = easingObject; + + // Any defined value transformation must be applied + Tweenable.applyFilter(mockTweenable, 'tweenCreated'); + Tweenable.applyFilter(mockTweenable, 'beforeTween'); + + var interpolatedValues = getInterpolatedValues( + from, current, targetState, position, easingObject); + + // Transform values back into their original format + Tweenable.applyFilter(mockTweenable, 'afterTween'); + + return interpolatedValues; + }; + + }()); + + /** + * Adds string interpolation support to Shifty. + * + * The Token extension allows Shifty to tween numbers inside of strings. Among + * other things, this allows you to animate CSS properties. For example, you + * can do this: + * + * var tweenable = new Tweenable(); + * tweenable.tween({ + * from: { transform: 'translateX(45px)'}, + * to: { transform: 'translateX(90xp)'} + * }); + * + * ` ` + * `translateX(45)` will be tweened to `translateX(90)`. To demonstrate: + * + * var tweenable = new Tweenable(); + * tweenable.tween({ + * from: { transform: 'translateX(45px)'}, + * to: { transform: 'translateX(90px)'}, + * step: function (state) { + * console.log(state.transform); + * } + * }); + * + * ` ` + * The above snippet will log something like this in the console: + * + * translateX(60.3px) + * ... + * translateX(76.05px) + * ... + * translateX(90px) + * + * ` ` + * Another use for this is animating colors: + * + * var tweenable = new Tweenable(); + * tweenable.tween({ + * from: { color: 'rgb(0,255,0)'}, + * to: { color: 'rgb(255,0,255)'}, + * step: function (state) { + * console.log(state.color); + * } + * }); + * + * ` ` + * The above snippet will log something like this: + * + * rgb(84,170,84) + * ... + * rgb(170,84,170) + * ... + * rgb(255,0,255) + * + * ` ` + * This extension also supports hexadecimal colors, in both long (`#ff00ff`) + * and short (`#f0f`) forms. Be aware that hexadecimal input values will be + * converted into the equivalent RGB output values. This is done to optimize + * for performance. + * + * var tweenable = new Tweenable(); + * tweenable.tween({ + * from: { color: '#0f0'}, + * to: { color: '#f0f'}, + * step: function (state) { + * console.log(state.color); + * } + * }); + * + * ` ` + * This snippet will generate the same output as the one before it because + * equivalent values were supplied (just in hexadecimal form rather than RGB): + * + * rgb(84,170,84) + * ... + * rgb(170,84,170) + * ... + * rgb(255,0,255) + * + * ` ` + * ` ` + * ## Easing support + * + * Easing works somewhat differently in the Token extension. This is because + * some CSS properties have multiple values in them, and you might need to + * tween each value along its own easing curve. A basic example: + * + * var tweenable = new Tweenable(); + * tweenable.tween({ + * from: { transform: 'translateX(0px) translateY(0px)'}, + * to: { transform: 'translateX(100px) translateY(100px)'}, + * easing: { transform: 'easeInQuad' }, + * step: function (state) { + * console.log(state.transform); + * } + * }); + * + * ` ` + * The above snippet create values like this: + * + * translateX(11.560000000000002px) translateY(11.560000000000002px) + * ... + * translateX(46.24000000000001px) translateY(46.24000000000001px) + * ... + * translateX(100px) translateY(100px) + * + * ` ` + * In this case, the values for `translateX` and `translateY` are always the + * same for each step of the tween, because they have the same start and end + * points and both use the same easing curve. We can also tween `translateX` + * and `translateY` along independent curves: + * + * var tweenable = new Tweenable(); + * tweenable.tween({ + * from: { transform: 'translateX(0px) translateY(0px)'}, + * to: { transform: 'translateX(100px) translateY(100px)'}, + * easing: { transform: 'easeInQuad bounce' }, + * step: function (state) { + * console.log(state.transform); + * } + * }); + * + * ` ` + * The above snippet create values like this: + * + * translateX(10.89px) translateY(82.355625px) + * ... + * translateX(44.89000000000001px) translateY(86.73062500000002px) + * ... + * translateX(100px) translateY(100px) + * + * ` ` + * `translateX` and `translateY` are not in sync anymore, because `easeInQuad` + * was specified for `translateX` and `bounce` for `translateY`. Mixing and + * matching easing curves can make for some interesting motion in your + * animations. + * + * The order of the space-separated easing curves correspond the token values + * they apply to. If there are more token values than easing curves listed, + * the last easing curve listed is used. + */ + function token () { + // Functionality for this extension runs implicitly if it is loaded. + } /*!*/ + + // token function is defined above only so that dox-foundation sees it as + // documentation and renders it. It is never used, and is optimized away at + // build time. + + ;(function (Tweenable) { + + /*! + * @typedef {{ + * formatString: string + * chunkNames: Array.<string> + * }} + */ + var formatManifest; + + // CONSTANTS + + var R_NUMBER_COMPONENT = /(\d|\-|\.)/; + var R_FORMAT_CHUNKS = /([^\-0-9\.]+)/g; + var R_UNFORMATTED_VALUES = /[0-9.\-]+/g; + var R_RGB = new RegExp( + 'rgb\\(' + R_UNFORMATTED_VALUES.source + + (/,\s*/.source) + R_UNFORMATTED_VALUES.source + + (/,\s*/.source) + R_UNFORMATTED_VALUES.source + '\\)', 'g'); + var R_RGB_PREFIX = /^.*\(/; + var R_HEX = /#([0-9]|[a-f]){3,6}/gi; + var VALUE_PLACEHOLDER = 'VAL'; + + // HELPERS + + var getFormatChunksFrom_accumulator = []; + /*! + * @param {Array.number} rawValues + * @param {string} prefix + * + * @return {Array.<string>} + */ + function getFormatChunksFrom (rawValues, prefix) { + getFormatChunksFrom_accumulator.length = 0; + + var rawValuesLength = rawValues.length; + var i; + + for (i = 0; i < rawValuesLength; i++) { + getFormatChunksFrom_accumulator.push('_' + prefix + '_' + i); + } + + return getFormatChunksFrom_accumulator; + } + + /*! + * @param {string} formattedString + * + * @return {string} + */ + function getFormatStringFrom (formattedString) { + var chunks = formattedString.match(R_FORMAT_CHUNKS); + + if (!chunks) { + // chunks will be null if there were no tokens to parse in + // formattedString (for example, if formattedString is '2'). Coerce + // chunks to be useful here. + chunks = ['', '']; + + // If there is only one chunk, assume that the string is a number + // followed by a token... + // NOTE: This may be an unwise assumption. + } else if (chunks.length === 1 || + // ...or if the string starts with a number component (".", "-", or a + // digit)... + formattedString[0].match(R_NUMBER_COMPONENT)) { + // ...prepend an empty string here to make sure that the formatted number + // is properly replaced by VALUE_PLACEHOLDER + chunks.unshift(''); + } + + return chunks.join(VALUE_PLACEHOLDER); + } + + /*! + * Convert all hex color values within a string to an rgb string. + * + * @param {Object} stateObject + * + * @return {Object} The modified obj + */ + function sanitizeObjectForHexProps (stateObject) { + Tweenable.each(stateObject, function (prop) { + var currentProp = stateObject[prop]; + + if (typeof currentProp === 'string' && currentProp.match(R_HEX)) { + stateObject[prop] = sanitizeHexChunksToRGB(currentProp); + } + }); + } + + /*! + * @param {string} str + * + * @return {string} + */ + function sanitizeHexChunksToRGB (str) { + return filterStringChunks(R_HEX, str, convertHexToRGB); + } + + /*! + * @param {string} hexString + * + * @return {string} + */ + function convertHexToRGB (hexString) { + var rgbArr = hexToRGBArray(hexString); + return 'rgb(' + rgbArr[0] + ',' + rgbArr[1] + ',' + rgbArr[2] + ')'; + } + + var hexToRGBArray_returnArray = []; + /*! + * Convert a hexadecimal string to an array with three items, one each for + * the red, blue, and green decimal values. + * + * @param {string} hex A hexadecimal string. + * + * @returns {Array.<number>} The converted Array of RGB values if `hex` is a + * valid string, or an Array of three 0's. + */ + function hexToRGBArray (hex) { + + hex = hex.replace(/#/, ''); + + // If the string is a shorthand three digit hex notation, normalize it to + // the standard six digit notation + if (hex.length === 3) { + hex = hex.split(''); + hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; + } + + hexToRGBArray_returnArray[0] = hexToDec(hex.substr(0, 2)); + hexToRGBArray_returnArray[1] = hexToDec(hex.substr(2, 2)); + hexToRGBArray_returnArray[2] = hexToDec(hex.substr(4, 2)); + + return hexToRGBArray_returnArray; + } + + /*! + * Convert a base-16 number to base-10. + * + * @param {Number|String} hex The value to convert + * + * @returns {Number} The base-10 equivalent of `hex`. + */ + function hexToDec (hex) { + return parseInt(hex, 16); + } + + /*! + * Runs a filter operation on all chunks of a string that match a RegExp + * + * @param {RegExp} pattern + * @param {string} unfilteredString + * @param {function(string)} filter + * + * @return {string} + */ + function filterStringChunks (pattern, unfilteredString, filter) { + var pattenMatches = unfilteredString.match(pattern); + var filteredString = unfilteredString.replace(pattern, VALUE_PLACEHOLDER); + + if (pattenMatches) { + var pattenMatchesLength = pattenMatches.length; + var currentChunk; + + for (var i = 0; i < pattenMatchesLength; i++) { + currentChunk = pattenMatches.shift(); + filteredString = filteredString.replace( + VALUE_PLACEHOLDER, filter(currentChunk)); + } + } + + return filteredString; + } + + /*! + * Check for floating point values within rgb strings and rounds them. + * + * @param {string} formattedString + * + * @return {string} + */ + function sanitizeRGBChunks (formattedString) { + return filterStringChunks(R_RGB, formattedString, sanitizeRGBChunk); + } + + /*! + * @param {string} rgbChunk + * + * @return {string} + */ + function sanitizeRGBChunk (rgbChunk) { + var numbers = rgbChunk.match(R_UNFORMATTED_VALUES); + var numbersLength = numbers.length; + var sanitizedString = rgbChunk.match(R_RGB_PREFIX)[0]; + + for (var i = 0; i < numbersLength; i++) { + sanitizedString += parseInt(numbers[i], 10) + ','; + } + + sanitizedString = sanitizedString.slice(0, -1) + ')'; + + return sanitizedString; + } + + /*! + * @param {Object} stateObject + * + * @return {Object} An Object of formatManifests that correspond to + * the string properties of stateObject + */ + function getFormatManifests (stateObject) { + var manifestAccumulator = {}; + + Tweenable.each(stateObject, function (prop) { + var currentProp = stateObject[prop]; + + if (typeof currentProp === 'string') { + var rawValues = getValuesFrom(currentProp); + + manifestAccumulator[prop] = { + 'formatString': getFormatStringFrom(currentProp) + ,'chunkNames': getFormatChunksFrom(rawValues, prop) + }; + } + }); + + return manifestAccumulator; + } + + /*! + * @param {Object} stateObject + * @param {Object} formatManifests + */ + function expandFormattedProperties (stateObject, formatManifests) { + Tweenable.each(formatManifests, function (prop) { + var currentProp = stateObject[prop]; + var rawValues = getValuesFrom(currentProp); + var rawValuesLength = rawValues.length; + + for (var i = 0; i < rawValuesLength; i++) { + stateObject[formatManifests[prop].chunkNames[i]] = +rawValues[i]; + } + + delete stateObject[prop]; + }); + } + + /*! + * @param {Object} stateObject + * @param {Object} formatManifests + */ + function collapseFormattedProperties (stateObject, formatManifests) { + Tweenable.each(formatManifests, function (prop) { + var currentProp = stateObject[prop]; + var formatChunks = extractPropertyChunks( + stateObject, formatManifests[prop].chunkNames); + var valuesList = getValuesList( + formatChunks, formatManifests[prop].chunkNames); + currentProp = getFormattedValues( + formatManifests[prop].formatString, valuesList); + stateObject[prop] = sanitizeRGBChunks(currentProp); + }); + } + + /*! + * @param {Object} stateObject + * @param {Array.<string>} chunkNames + * + * @return {Object} The extracted value chunks. + */ + function extractPropertyChunks (stateObject, chunkNames) { + var extractedValues = {}; + var currentChunkName, chunkNamesLength = chunkNames.length; + + for (var i = 0; i < chunkNamesLength; i++) { + currentChunkName = chunkNames[i]; + extractedValues[currentChunkName] = stateObject[currentChunkName]; + delete stateObject[currentChunkName]; + } + + return extractedValues; + } + + var getValuesList_accumulator = []; + /*! + * @param {Object} stateObject + * @param {Array.<string>} chunkNames + * + * @return {Array.<number>} + */ + function getValuesList (stateObject, chunkNames) { + getValuesList_accumulator.length = 0; + var chunkNamesLength = chunkNames.length; + + for (var i = 0; i < chunkNamesLength; i++) { + getValuesList_accumulator.push(stateObject[chunkNames[i]]); + } + + return getValuesList_accumulator; + } + + /*! + * @param {string} formatString + * @param {Array.<number>} rawValues + * + * @return {string} + */ + function getFormattedValues (formatString, rawValues) { + var formattedValueString = formatString; + var rawValuesLength = rawValues.length; + + for (var i = 0; i < rawValuesLength; i++) { + formattedValueString = formattedValueString.replace( + VALUE_PLACEHOLDER, +rawValues[i].toFixed(4)); + } + + return formattedValueString; + } + + /*! + * Note: It's the duty of the caller to convert the Array elements of the + * return value into numbers. This is a performance optimization. + * + * @param {string} formattedString + * + * @return {Array.<string>|null} + */ + function getValuesFrom (formattedString) { + return formattedString.match(R_UNFORMATTED_VALUES); + } + + /*! + * @param {Object} easingObject + * @param {Object} tokenData + */ + function expandEasingObject (easingObject, tokenData) { + Tweenable.each(tokenData, function (prop) { + var currentProp = tokenData[prop]; + var chunkNames = currentProp.chunkNames; + var chunkLength = chunkNames.length; + var easingChunks = easingObject[prop].split(' '); + var lastEasingChunk = easingChunks[easingChunks.length - 1]; + + for (var i = 0; i < chunkLength; i++) { + easingObject[chunkNames[i]] = easingChunks[i] || lastEasingChunk; + } + + delete easingObject[prop]; + }); + } + + /*! + * @param {Object} easingObject + * @param {Object} tokenData + */ + function collapseEasingObject (easingObject, tokenData) { + Tweenable.each(tokenData, function (prop) { + var currentProp = tokenData[prop]; + var chunkNames = currentProp.chunkNames; + var chunkLength = chunkNames.length; + var composedEasingString = ''; + + for (var i = 0; i < chunkLength; i++) { + composedEasingString += ' ' + easingObject[chunkNames[i]]; + delete easingObject[chunkNames[i]]; + } + + easingObject[prop] = composedEasingString.substr(1); + }); + } + + Tweenable.prototype.filter.token = { + 'tweenCreated': function (currentState, fromState, toState, easingObject) { + sanitizeObjectForHexProps(currentState); + sanitizeObjectForHexProps(fromState); + sanitizeObjectForHexProps(toState); + this._tokenData = getFormatManifests(currentState); + }, + + 'beforeTween': function (currentState, fromState, toState, easingObject) { + expandEasingObject(easingObject, this._tokenData); + expandFormattedProperties(currentState, this._tokenData); + expandFormattedProperties(fromState, this._tokenData); + expandFormattedProperties(toState, this._tokenData); + }, + + 'afterTween': function (currentState, fromState, toState, easingObject) { + collapseFormattedProperties(currentState, this._tokenData); + collapseFormattedProperties(fromState, this._tokenData); + collapseFormattedProperties(toState, this._tokenData); + collapseEasingObject(easingObject, this._tokenData); + } + }; + + } (Tweenable)); + + }(window)); + + return window.Tweenable; +}); diff --git a/www/lib/angular-carousel/src/directives/sliceFilter.js b/www/lib/angular-carousel/src/directives/sliceFilter.js new file mode 100644 index 00000000..f3121bf3 --- /dev/null +++ b/www/lib/angular-carousel/src/directives/sliceFilter.js @@ -0,0 +1,17 @@ +(function() { + "use strict"; + + angular.module('angular-carousel') + + .filter('carouselSlice', function() { + return function(collection, start, size) { + if (angular.isArray(collection)) { + return collection.slice(start, start + size); + } else if (angular.isObject(collection)) { + // dont try to slice collections :) + return collection; + } + }; + }); + +})(); diff --git a/www/lib/angular-carousel/test/karma.conf.js b/www/lib/angular-carousel/test/karma.conf.js new file mode 100644 index 00000000..00f32b69 --- /dev/null +++ b/www/lib/angular-carousel/test/karma.conf.js @@ -0,0 +1,71 @@ +var grunt = require('grunt'); +module.exports = function ( karma ) { + karma.set({ + /** + * From where to look for files, starting with the location of this file. + */ + basePath: './..', + + /** + * This is the list of file patterns to load into the browser during testing. + */ + files: [ + 'bower_components/angular/angular.js', + 'bower_components/angular-touch/angular-touch.js', + 'bower_components/angular-mocks/angular-mocks.js', + 'bower_components/requirejs/require.js', + 'dist/angular-carousel.js', + 'test/unit/*.js' + ], + + frameworks: [ 'jasmine' ], + plugins: [ 'karma-jasmine', 'karma-firefox-launcher', 'karma-chrome-launcher', 'karma-coverage'], + + logLevel: 'DEBUG', + /** + * How to report, by default. + */ + reporters: ['dots', 'coverage'], + + preprocessors: { + // source files, that you wanna generate coverage for + // do not include tests or libraries + // (these files will be instrumented by Istanbul) + '../dist/*.js': ['coverage'] + }, + + coverageReporter: { + type : 'html', + dir : 'coverage/' + }, + + /** + * On which port should the browser connect, on which port is the test runner + * operating, and what is the URL path for the browser to use. + */ + port: 7018, + runnerPort: 7100, + urlRoot: '/', + + /** + * Disable file watching by default. + */ + autoWatch: false, + + /** + * The list of browsers to launch to test ondest * default, but other browser names include: + * Chrome, ChromeCanary, Firefox, Opera, Safari, PhantomJS + * + * Note that you can also use the executable name of the browser, like "chromium" + * or "firefox", but that these vary based on your operating system. + * + * You may also leave this blank and manually navigate your browser to + * http://localhost:9018/ when you're running tests. The window/tab can be left + * open and the tests will automatically occur there during the build. This has + * the aesthetic advantage of not launching a browser every time you save. + */ + browsers: [ + 'Chrome' + ] + }); +}; diff --git a/www/lib/angular-carousel/test/unit/angular-carousel.js b/www/lib/angular-carousel/test/unit/angular-carousel.js new file mode 100755 index 00000000..5b2031da --- /dev/null +++ b/www/lib/angular-carousel/test/unit/angular-carousel.js @@ -0,0 +1,624 @@ +/*global waits, runs, iit, browserTrigger, beforeEach, afterEach, describe, it, inject, expect, module, angular, $*/ + +describe('carousel', function () { + 'use strict'; + + var scope, $compile, $sandbox; + + + //$('body').append("<link href='/base/dist/angular-carousel.min.css' rel='stylesheet' type='text/css'>"); + /*$('body').append("<style>ul,li {padding:0;margin:0;width:200px !important} " + + ".rn-carousel-animate { -webkit-transition: -webkit-transform 0.001s ease-out; " + + "-moz-transition: -moz-transform 0.001s ease-out; transition: transform 0.001s ease-out;} "+ + ".rn-carousel-noanimate {-webkit-transition: none;-moz-transition: none;-ms-transition: none;" + + "-o-transition: none;transition: none;}</style>");*/ + + //console.log(document.location); + beforeEach( + module('angular-carousel') + ); + + beforeEach(inject(function ($rootScope, _$compile_) { + scope = $rootScope; + $compile = _$compile_; + // $('body').css({ + // padding: 0, + // margin:0 + // }); + // $sandbox = $('<div id="sandbox"></div>').appendTo($('body')); + })); + + afterEach(function() { + //$sandbox.remove(); + scope.$destroy(); + }); + + function compileTpl(overrideOptions) { + var options = { + useIndex: false, + useIndicator: false, + useControl: false, + useBuffer: false, + nbItems: 25, + useWatch: false + }; + if (overrideOptions) angular.extend(options, overrideOptions); + var sampleData = { + scope: { + items: [], + localIndex: 5 + } + }; + for (var i=0; i<options.nbItems; i++) { + sampleData.scope.items.push({ + text: 'slide #' + i, + id: i + }); + } + var tpl = '<ul rn-carousel '; + if (options.useIndicator) tpl += ' rn-carousel-indicator '; + if (options.useControl) tpl += ' rn-carousel-control '; + if (options.useBuffer) tpl += ' rn-carousel-buffered '; + if (options.useWatch) tpl += ' rn-carousel-watch '; + if (options.useIndex) tpl += ' rn-carousel-index="' + options.useIndex + '" '; + tpl += '><li class="test" style="width:200px" ng-repeat="item in items" id="slide-{{ item.id }}">{{ item.text }}</li></ul>'; + angular.extend(scope, sampleData.scope); + // var $element = $(tpl).appendTo($sandbox); + var $element = $compile(tpl)(scope); + scope.$digest(); + return $element; + } + + function getElmTransform(elm) { + var curMatrix = elm.css('-webkit-transform'); + if (!curMatrix) curMatrix = elm.css('transform'); + return curMatrix; + } + function validCSStransform(elm) { + var expectedPosition = (elm.offsetWidth * elm.scope().carouselCollection.index * -1), + expectedMatrix = 'matrix(1, 0, 0, 1, ' + expectedPosition + ', 0)', + curMatrix = getElmTransform(elm); + expect(curMatrix).toBe(expectedMatrix); + } +/* + it('should load test', function() { + expect(1).toBe(1); + }); + + describe('directive', function () { + it('should add a wrapper div around the ul/li', function () { + var elm = compileTpl(); + expect(elm.parent().hasClass('rn-carousel-container')).toBe(true); + }); + it('should add a class to the ul', function () { + var elm = compileTpl(); + expect(elm.hasClass('rn-carousel-slides')).toBe(true); + }); + it('should have enough slides', function () { + var elm = compileTpl(); + expect(elm.find('li').length).toBe(scope.items.length); + }); + it('generated container outerWidth should match the ul outerWidth', function () { + var elm = compileTpl(); + expect(elm.parent()[0].offsetWidth).toBe(elm[0].offsetWidth); + }); + }); + + describe('directive with a data-bound index defined', function () { + it('the index attribute should be used to position the first visible slide', function () { + var elm = compileTpl({useIndex: 'localIndex'}); + waitAndCheck(function() { + validCSStransform(elm); + }, 200); + }); + it('index change should update the carousel position', function () { + var elm = compileTpl({useIndex: 'localIndex'}); + scope.localIndex = 5; + scope.$digest(); + waitAndCheck(function() { + validCSStransform(elm); + }, 200); + }); + it('carousel index should be bound to local index', function () { + var elm = compileTpl({useIndex: 'localIndex'}); + scope.localIndex = 5; + scope.$digest(); + expect(elm.scope().carouselCollection.index).toBe(scope.localIndex); + }); + }); + + describe('directive with a numeric index defined', function () { + it('the index attribute should be used to position the first visible slide', function () { + var elm = compileTpl({useIndex: 5}); + waitAndCheck(function() { + validCSStransform(elm); + }, 200); + }); + it('index change should update the carousel position', function () { + // check watcher present even if index is not a bindable attribute + var elm = compileTpl({useIndex: 5}); + elm.scope().carouselCollection.goToIndex(9); + scope.$digest(); + waitAndCheck(function() { + validCSStransform(elm); + }, 200); + }); + it('index out of range should set the carousel to last slide', function () { + var elm = compileTpl({useIndex: 100}); + expect(elm.scope().carouselCollection.index).toBe(scope.items.length - 1); + expect(elm.find('li').length).toBe(scope.items.length); + expect(elm.find('li:last')[0].id).toBe('slide-' + (scope.items.length - 1)); + }); + it('negative index should set the carousel to first slide', function () { + var elm = compileTpl({useIndex: -100}); + expect(elm.scope().carouselCollection.index).toBe(0); + expect(elm.find('li').length).toBe(scope.items.length); + expect(elm.find('li')[0].id).toBe('slide-0'); + }); + }); + + describe('directive with no index defined', function () { + it('should add a wrapper div around the ul/li', function () { + var elm = compileTpl({useIndex:false}); + expect(elm.parent().hasClass('rn-carousel-container')).toBe(true); + }); + it('should add a class to the ul', function () { + var elm = compileTpl({useIndex:false}); + expect(elm.hasClass('rn-carousel-slides')).toBe(true); + }); + it('should have enough slides', function () { + var elm = compileTpl({useIndex:false}); + expect(elm.find('li').length).toBe(scope.items.length); + }); + it('generated container outerWidth should match the ul outerWidth', function () { + var elm = compileTpl({useIndex:false}); + expect(elm.parent().outerWidth()).toBe(elm.outerWidth()); + }); + it('the index attribute should be used to position the first visible slide', function () { + var elm = compileTpl({useIndex:false}); + validCSStransform(elm); + }); + }); + + describe('indicator directive', function () { + it('should add an indicator div', function () { + var elm = compileTpl({useIndicator: true}); + expect(elm.parent().find('.rn-carousel-indicator').length).toBe(1); + }); + it('should add enough indicators', function () { + var elm = compileTpl({useIndicator: true}); + expect(elm.parent().find('.rn-carousel-indicator span').length).toBe(scope.items.length); + }); + it('should have an active indicator based on the carousel index', function () { + var elm = compileTpl({useIndicator: true}); + expect(elm.parent().find('.rn-carousel-indicator span:nth-of-type(' + (elm.scope().carouselCollection.index + 1) + ')').hasClass('active')).toBe(true); + }); + it('should update the active indicator when local index changes', function () { + var elm = compileTpl({useIndicator: true, useIndex: 'localIndex'}); + scope.localIndex = 2; + scope.$digest(); + expect(elm.parent().find('.rn-carousel-indicator span:nth-of-type(' + (scope.localIndex + 1) + ')').hasClass('active')).toBe(true); + }); + }); + + describe('controls directive', function () { + it('should add an controls div', function () { + var elm = compileTpl({useControl: true}); + expect(elm.parent().find('.rn-carousel-controls').length).toBe(1); + }); + it('should have next control but not back', function () { + var elm = compileTpl({useControl: true}); + expect(elm.parent().find('.rn-carousel-control-next').length).toBe(1); + expect(elm.parent().find('.rn-carousel-control-back').length).toBe(0); + }); + it('should have next and back controls when local index changes', function () { + var elm = compileTpl({useControl: true, useIndex: 'localIndex'}); + scope.localIndex = 1; + scope.$digest(); + expect(elm.parent().find('.rn-carousel-control').length).toBe(2); + }); + it('should have only back controls when local index is at the end', function () { + var elm = compileTpl({useControl: true, useIndex: 'localIndex'}); + scope.localIndex = scope.items.length - 1; + scope.$digest(); + expect(elm.parent().find('.rn-carousel-control-next').length).toBe(0); + expect(elm.parent().find('.rn-carousel-control-back').length).toBe(1); + }); + }); + + describe('directive with no index defined', function () { + it('should add a wrapper div around the ul/li', function () { + var elm = compileTpl({useIndex:false}); + expect(elm.parent().hasClass('rn-carousel-container')).toBe(true); + }); + it('should add a class to the ul', function () { + var elm = compileTpl({useIndex:false}); + expect(elm.hasClass('rn-carousel-slides')).toBe(true); + }); + it('should have enough slides', function () { + var elm = compileTpl({useIndex:false}); + expect(elm.find('li').length).toBe(scope.items.length); + }); + it('generated container outerWidth should match the ul outerWidth', function () { + var elm = compileTpl({useIndex:false}); + expect(elm.parent().outerWidth()).toBe(elm.outerWidth()); + }); + it('the index attribute should be used to position the first visible slide', function () { + var elm = compileTpl({useIndex:false}); + validCSStransform(elm); + }); + }); + + describe('buffered carousel', function () { + it('should minimize the DOM', function () { + var elm = compileTpl({useBuffer: true}); + expect(elm.find('li').length).toBe(3); + }); + // TODO + it('should position the buffered slides correctly', function () { + var elm = compileTpl({useBuffer: true, useIndex: 'localIndex'}); + scope.localIndex = 5; + scope.$digest(); + expect(elm.find('li')[0].id).toBe('slide-' + (scope.localIndex - 1)); + }); + it('should position the buffered slides correctly even if index is zero', function () { + var elm = compileTpl({useBuffer: true, useIndex: '0'}); + expect(elm.find('li').length).toBe(3); + expect(elm.find('li')[0].id).toBe('slide-0'); + }); + it('should position the buffered slides correctly with a out of range index', function () { + var elm = compileTpl({useBuffer: true, useIndex: '100'}); + expect(elm.scope().carouselCollection.index).toBe(scope.items.length - 1); + var firstId = scope.items.length - 3; + expect(elm.find('li').length).toBe(3); + expect(elm.find('li')[0].id).toBe('slide-' + firstId); + expect(elm.find('li:last')[0].id).toBe('slide-' + (firstId + 3 - 1)); + }); + it('should position the buffered slides correctly with a negative index', function () { + var elm = compileTpl({useBuffer: true, useIndex: '-100'}); + expect(elm.scope().carouselCollection.index).toBe(0); + expect(elm.find('li').length).toBe(3); + expect(elm.find('li')[0].id).toBe('slide-0'); + expect(elm.find('li:last')[0].id).toBe('slide-' + (3 - 1)); + }); + }); + + describe('index property on standard carousel', function () { + it('should be at 0 on start', function () { + var elm = compileTpl(); + expect(elm.scope().carouselCollection.index).toBe(0); + }); + it('should be set at initial position', function () { + var elm = compileTpl({useIndex: 'localIndex'}); + expect(elm.scope().carouselCollection.index).toBe(scope.localIndex); + }); + it('should follow carousel position', function () { + var elm = compileTpl({useIndex: 'localIndex'}); + scope.localIndex = scope.items.length - 1; + scope.$digest(); + expect(elm.scope().carouselCollection.index).toBe(scope.items.length - 1); + }); + }); + + describe('index property on buffered carousel', function () { + it('should be at 0 on start', function () { + var elm = compileTpl({useBuffer: true}); + expect(elm.find('li')[0].id).toBe('slide-0'); + expect(elm.scope().carouselCollection.index).toBe(0); + }); + it('should be set correctly at initial position', function () { + var elm = compileTpl({useBuffer: true, useIndex: 'localIndex'}); + expect(elm.scope().carouselCollection.index).toBe(scope.localIndex); + expect(elm.find('li')[0].id).toBe('slide-' + (scope.localIndex - 1)); + }); + it('should be last item of buffer if carousel last slide', function () { + var elm = compileTpl({useBuffer: true, useIndex: 'localIndex'}); + scope.localIndex = scope.items.length - 1; + scope.$digest(); + waitAndCheck(function() { + expect(elm.scope().carouselCollection.index).toBe(scope.localIndex); + expect(elm.find('li')[0].id).toBe('slide-' + (scope.localIndex - 2)); + }); + }); + it('should be last item of buffer if carousel last slide', function () { + var elm = compileTpl({useBuffer: true, useIndex: 'localIndex'}); + scope.localIndex = 100; + scope.$digest(); + waitAndCheck(function() { + expect(elm.scope().carouselCollection.index).toBe(scope.items.length - 1); + expect(elm.find('li')[0].id).toBe('slide-' + (scope.localIndex-2)); + }); + }); + it('should display first slide when reset local index to 0', function () { + var elm = compileTpl({useBuffer: true, useIndex: 'localIndex'}); + scope.localIndex = 5; + scope.$digest(); + scope.localIndex = 0; + scope.$digest(); + expect(elm.position().left).toBe(0); + expect(elm.css('left')).toBe('auto'); + }); + }); + + // TODO + // describe('collection update', function () { + // it('standard carousel should display first slide when we reset the collection', function () { + // var elm = compileTpl({useIndex: 'localIndex'}); + // scope.localIndex = 5; + // scope.$digest(); + // scope.items = [{id:1}, {id:2}]; + // scope.$digest(); + // expect(elm.position().left).toBe(0); + // expect(elm.css('left')).toBe('auto'); + // expect(elm.scope().activeIndex).toBe(0); + // }); + // it('buffered carousel should display first slide when we reset the collection', function () { + // var elm = compileTpl({useBuffer: true, useIndex: 'localIndex'}); + // scope.localIndex = 5; + // scope.$digest(); + // scope.items = [{id:1}, {id:2}]; + // scope.$digest(); + // expect(elm.position().left).toBe(0); + // expect(elm.css('left')).toBe('auto'); + // expect(elm.scope().activeIndex).toBe(0); + // }); + // }); + + function fakeMove(elm, distance) { + // trigger a carousel swipe movement + var startX = 100, + startY = 10, + endX = distance + startX; + + browserTrigger(elm, 'touchstart', [], startX, startY); + browserTrigger(elm, 'touchmove', [], endX, startY); + browserTrigger(elm, 'touchmove', [], endX, startY); + browserTrigger(elm, 'touchend', [], endX, startY); + } + function waitAndCheck(cb, delay) { + waits(delay || 100); + runs(cb); + } + describe('swipe behaviour', function () { + var minMove; + beforeEach(function() { + minMove = 31; + }); + it('should not show prev slide if swipe backwards at index 0', function() { + // yes, backwards swipe means positive pixels count :) + var elm = compileTpl(); + fakeMove(elm, minMove); + expect(elm.scope().carouselCollection.index).toBe(0); + }); + it('should not show next slide if swipe forward at last slide', function() { + var elm = compileTpl(); + elm.scope().carouselCollection.goToIndex(scope.items.length - 1); + fakeMove(elm, -minMove); + expect(elm.scope().carouselCollection.index).toBe(scope.items.length - 1); + }); + it('should move slide backward if backwards swipe at index > 0', function() { + var elm = compileTpl({useIndex: 1}); + fakeMove(elm, minMove); + expect(elm.scope().carouselCollection.index).toBe(0); + }); + it('should move to next slide on swipe forward', function() { + var elm = compileTpl(); + fakeMove(elm, -minMove); + expect(elm.scope().carouselCollection.index).toBe(1); + }); + it('should not move to next slide on too little swipe forward', function() { + var elm = compileTpl(); + fakeMove(elm, -12); + expect(elm.scope().carouselCollection.index).toBe(0); + }); + it('should not move to prev slide on too little swipe backward', function() { + var elm = compileTpl({useIndex: 1}); + fakeMove(elm, 12); + expect(elm.scope().carouselCollection.index).toBe(1); + }); + it('should follow multiple moves', function() { + var elm = compileTpl(); + // var minMove = -(elm.outerWidth() * 0.1 + 1); + fakeMove(elm, -minMove); + //console.log(minMove, elm.scope().carouselCollection.index); + fakeMove(elm,-minMove); + fakeMove(elm, -minMove); + expect(elm.scope().carouselCollection.index).toBe(3); + fakeMove(elm, minMove); + fakeMove(elm, minMove); + expect(elm.scope().carouselCollection.index).toBe(1); + fakeMove(elm, minMove); + fakeMove(elm, minMove); + fakeMove(elm, minMove); + expect(elm.scope().carouselCollection.index).toBe(0); + }); + }); + + describe('swipe buffered behaviour', function () { + it('should follow multiple moves and buffer accordingly', function() { + var elm = compileTpl({useBuffer: true}); + var minMove = -(elm.outerWidth() * 0.1 + 1); + fakeMove(elm, minMove); + + waitAndCheck(function() { + expect(elm.scope().carouselCollection.index).toBe(1); + expect(elm.find('li')[0].id).toBe('slide-0'); + fakeMove(elm, minMove); + waitAndCheck(function() { + expect(elm.scope().carouselCollection.index).toBe(2); + expect(elm.find('li')[0].id).toBe('slide-1'); + fakeMove(elm, -minMove); + waitAndCheck(function() { + expect(elm.scope().carouselCollection.index).toBe(1); + expect(elm.find('li')[0].id).toBe('slide-0'); + fakeMove(elm, -minMove); + waitAndCheck(function() { + expect(elm.scope().carouselCollection.index).toBe(0); + expect(elm.find('li')[0].id).toBe('slide-0'); + }); + }); + }); + }); + }); + }); + + describe('collection watch', function () { + describe('standard watch (no deep)', function () { + it('it should display first slide when we reset the collection', function () { + var elm = compileTpl({useIndex: 'localIndex'}); + scope.localIndex = 5; + scope.$digest(); + expect(elm.scope().carouselCollection.index).toBe(5); + scope.items = [{id:1}, {id:2}]; + scope.$digest(); + expect(elm.position().left).toBe(0); + expect(elm.css('left')).toBe('auto'); + expect(elm.scope().carouselCollection.index).toBe(0); + }); + it('should NOT update slides when collection changes partially', function() { + var elm = compileTpl(); + var originalLength = scope.items.length; + expect(elm.find('li').length).toBe(originalLength); + scope.items.push({'text': 'new item', 'id': 999}); + scope.$digest(); + expect(elm.find('li').length).toBe(originalLength); + expect(elm.find('li').last()[0].id).toBe('slide-' + (originalLength - 1)); + scope.items.pop(); + scope.items.pop(); + scope.items.pop(); + scope.$digest(); + expect(elm.find('li').length).toBe(originalLength); + expect(elm.find('li').last()[0].id).toBe('slide-' + (originalLength - 1)); + }); + }); + describe('standard watch (no deep) + buffer', function () { + it('it should display first slide when we reset the collection', function () { + var elm = compileTpl({useBuffer: true, useIndex: 'localIndex'}); + scope.localIndex = 5; + scope.$digest(); + expect(elm.scope().carouselCollection.index).toBe(5); + scope.items = [{id:1}, {id:2}]; + scope.$digest(); + expect(elm.position().left).toBe(0); + expect(elm.css('left')).toBe('auto'); + expect(elm.scope().carouselCollection.index).toBe(0); + }); + it('should NOT update slides when collection changes partially', function() { + var elm = compileTpl({useBuffer: true}); + var originalLength = elm.scope().carouselCollection.bufferSize; + expect(elm.find('li').length).toBe(originalLength); + scope.items.push({'text': 'new item', 'id': 999}); + scope.$digest(); + expect(elm.find('li').length).toBe(originalLength); + expect(elm.find('li').last()[0].id).toBe('slide-' + (originalLength - 1)); + scope.items.pop(); + scope.items.pop(); + scope.items.pop(); + scope.$digest(); + expect(elm.find('li').length).toBe(originalLength); + expect(elm.find('li').last()[0].id).toBe('slide-' + (originalLength - 1)); + }); + }); + describe('deep watch', function () { + it('should display first slide when we reset the collection', function () { + var elm = compileTpl({useIndex: 'localIndex', useWatch: true}); + scope.localIndex = 5; + scope.$digest(); + expect(elm.scope().carouselCollection.index).toBe(5); + scope.items = [{id:1}, {id:2}]; + scope.$digest(); + expect(elm.position().left).toBe(0); + expect(elm.css('left')).toBe('auto'); + expect(elm.scope().carouselCollection.index).toBe(0); + }); + it('should update slides when collection changes partially', function() { + var elm = compileTpl({useWatch: true}); + expect(elm.find('li').length).toBe(scope.items.length); + scope.items.push({'text': 'new item', 'id': 999}); + scope.$digest(); + expect(elm.find('li').length).toBe(scope.items.length); + expect(elm.find('li').last()[0].id).toBe('slide-999'); + scope.items.pop(); + scope.items.pop(); + scope.items.pop(); + scope.$digest(); + expect(elm.find('li').length).toBe(scope.items.length); + expect(elm.find('li').last()[0].id).toBe('slide-' + scope.items[scope.items.length - 1].id); + }); + }); + describe('deep watch + buffer', function () { + it('should display first slide when we reset the collection', function () { + var elm = compileTpl({userBuffer:true, useIndex: 'localIndex', useWatch: true}); + scope.localIndex = 5; + scope.$digest(); + expect(elm.scope().carouselCollection.index).toBe(5); + scope.items = [{id:1}, {id:2}]; + scope.$digest(); + expect(elm.position().left).toBe(0); + expect(elm.css('left')).toBe('auto'); + expect(elm.scope().carouselCollection.index).toBe(0); + }); + it('should update slides when collection changes partially', function() { + var elm = compileTpl({userBuffer:true, useWatch: true}); + expect(elm.find('li').length).toBe(scope.items.length); + scope.items.push({'text': 'new item', 'id': 999}); + scope.$digest(); + expect(elm.find('li').length).toBe(scope.items.length); + expect(elm.find('li').last()[0].id).toBe('slide-999'); + scope.items.pop(); + scope.items.pop(); + scope.items.pop(); + scope.$digest(); + expect(elm.find('li').length).toBe(scope.items.length); + expect(elm.find('li').last()[0].id).toBe('slide-' + scope.items[scope.items.length - 1].id); + }); + }); + + }); + // describe('delayed collection and index', function () { + // it('should follow multiple moves and buffer accordingly', function() { + + // describe('swipe buffered + index behaviour', function () { + // it('should initialise buffer start correctly when index is set', function() { + // var elm = compileTpl({useBuffer: true, useIndex: "localIndex", nbItems: 5}); + // scope.localIndex = 2; + // scope.$digest(); + // expect(elm.scope().carouselBufferStart).toBe(1); + // }); + // it('should initialise buffer start correctly when index is set at 0', function() { + // var elm = compileTpl({useBuffer: true, useIndex: "localIndex", nbItems: 5}); + // scope.localIndex = 0; + // scope.$digest(); + // expect(elm.scope().carouselBufferStart).toBe(0); + // }); + // it('should initialise buffer start correctly when index is set at last item', function() { + // var nbItems = 5; + // var elm = compileTpl({useBuffer: true, useIndex: "localIndex", nbItems: 5}); + // scope.localIndex = nbItems-1; + // scope.$digest(); + // console.log(elm.scope().activeIndex); + // waits(10); + // runs(function() { + // expect(elm.scope().carouselBufferStart).toBe(nbItems - elm.scope().carouselBufferSize); + // }); + // }); + // it('buffer position should update when local index changes', function() { + // var elm = compileTpl({useBuffer: true, useIndex: "localIndex", nbItems: 5}); + // scope.localIndex = 2; + // scope.$digest(); + // expect(elm.scope().carouselBufferStart).toBe(1); + // scope.localIndex = 3; + // scope.$digest(); + // waits(100); + // runs(function() { + // expect(elm.scope().carouselBufferStart).toBe(1); + // scope.localIndex = 0; + // scope.$digest(); + // expect(elm.scope().carouselBufferStart).toBe(0); + // }); + // }); + //}); +*/ + +}); + diff --git a/www/lib/angular-carousel/test/unit/angular-carousel.shifty.js b/www/lib/angular-carousel/test/unit/angular-carousel.shifty.js new file mode 100644 index 00000000..994169ce --- /dev/null +++ b/www/lib/angular-carousel/test/unit/angular-carousel.shifty.js @@ -0,0 +1,20 @@ +/*global waits, runs, iit, browserTrigger, beforeEach, afterEach, describe, it, inject, expect, module, angular, $*/ + +describe('angular-carousel.shifty', function () { + 'use strict'; + + describe("compatibility with requirejs", function(){ + var loadShifty = function() { + module('angular-carousel.shifty'); + }; + it("should not throw an exception when load the shifty within requirejs environment", function(){ + expect(loadShifty).not.toThrow(); + }); + + it("should not throw an exception when inject `Tweenable` within requirejs environment", function(){ + loadShifty(); + expect(function() {inject(function(Tweenable){});}).not.toThrow(); + }); + }); + +}); diff --git a/www/lib/angular-tooltips/.bower.json b/www/lib/angular-tooltips/.bower.json new file mode 100644 index 00000000..b8170005 --- /dev/null +++ b/www/lib/angular-tooltips/.bower.json @@ -0,0 +1,37 @@ +{ + "name": "angular-tooltips", + "version": "0.1.14", + "description": "A tooltips directive for angularjs.", + "authors": [ + "Filippo Oretti <filippo.oretti@gmail.com", + "Dario Andrei <wouldgo84@gmail.com>" + ], + "keywords": [ + "tooltip", + "tooltips", + "angularjs" + ], + "main": [ + "./dist/angular-tooltips.min.js", + "./dist/angular-tooltips.min.css" + ], + "license": "MIT", + "homepage": "http://720kb.github.io/angular-tooltips", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "_release": "0.1.14", + "_resolution": { + "type": "version", + "tag": "0.1.14", + "commit": "e2f3358f9bd9f7c94826a684ca749972820040c8" + }, + "_source": "git://github.com/720kb/angular-tooltips.git", + "_target": "~0.1.14", + "_originalSource": "angular-tooltips", + "_direct": true +} diff --git a/www/lib/angular-tooltips/Gruntfile.js b/www/lib/angular-tooltips/Gruntfile.js new file mode 100644 index 00000000..4271aa1b --- /dev/null +++ b/www/lib/angular-tooltips/Gruntfile.js @@ -0,0 +1,157 @@ +/*global module, require*/ +(function setUp(module, require) { + 'use strict'; + + var banner = ['/*!', + ' * Angular Tooltips v<%= pkg.version %>', + ' *', + ' * Released under the MIT license', + ' * www.opensource.org/licenses/MIT', + ' *', + ' * Brought to you by 720kb.net', + ' *', + ' * <%= grunt.template.today("yyyy-mm-dd") %>', + ' */\n\n'].join('\n') + , modRewrite = require('connect-modrewrite'); + + module.exports = function doGrunt(grunt) { + + grunt.initConfig({ + 'pkg': grunt.file.readJSON('package.json'), + 'confs': { + 'dist': 'dist', + 'config': 'config', + 'css': 'src/css', + 'js': 'src/js', + 'serverPort': 8000 + }, + 'csslint': { + 'options': { + 'csslintrc': '<%= confs.config %>/csslintrc.json' + }, + 'strict': { + 'src': [ + '<%= confs.css %>/**/*.css' + ] + } + }, + 'eslint': { + 'options': { + 'config': '<%= confs.config %>/eslint.json' + }, + 'target': [ + 'Gruntfile.js', + '<%= confs.js %>/**/*.js' + ] + }, + 'uglify': { + 'options': { + 'sourceMap': true, + 'sourceMapName': '<%= confs.dist %>/angular-tooltips.sourcemap.map', + 'preserveComments': false, + 'report': 'gzip', + 'banner': banner + }, + 'minifyTarget': { + 'files': { + '<%= confs.dist %>/angular-tooltips.min.js': [ + '<%= confs.js %>/angular-tooltips.js' + ] + } + } + }, + 'cssmin': { + 'options': { + 'report': 'gzip', + 'banner': banner + }, + 'minifyTarget': { + 'files': { + '<%= confs.dist %>/angular-tooltips.min.css': [ + '<%= confs.css %>/angular-tooltips.css' + ] + } + } + }, + 'connect': { + 'server': { + 'options': { + 'port': '<%= confs.serverPort %>', + 'base': '.', + 'keepalive': true, + 'middleware': function manageMiddlewares(connect, options) { + var middlewares = [] + , directory = options.directory || options.base[options.base.length - 1]; + + // enable Angular's HTML5 mode + middlewares.push(modRewrite(['!\\.html|\\.js|\\.svg|\\.css|\\.png|\\.gif$ /index.html [L]'])); + + if (!Array.isArray(options.base)) { + options.base = [options.base]; + } + options.base.forEach(function forEachOption(base) { + // Serve static files. + middlewares.push(connect.static(base)); + }); + + // Make directory browse-able. + middlewares.push(connect.directory(directory)); + + return middlewares; + } + } + } + }, + 'watch': { + 'dev': { + 'files': [ + 'Gruntfile.js', + '<%= confs.css %>/**/*.css', + '<%= confs.js %>/**/*.js' + ], + 'tasks': [ + 'csslint', + 'eslint' + ], + 'options': { + 'spawn': false + } + } + }, + 'concurrent': { + 'dev': { + 'tasks': [ + 'connect:server', + 'watch:dev' + ], + 'options': { + 'limit': '<%= concurrent.dev.tasks.length %>', + 'logConcurrentOutput': true + } + } + } + }); + + grunt.loadNpmTasks('grunt-contrib-csslint'); + grunt.loadNpmTasks('grunt-eslint'); + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-contrib-cssmin'); + + grunt.loadNpmTasks('grunt-concurrent'); + grunt.loadNpmTasks('grunt-contrib-connect'); + grunt.loadNpmTasks('grunt-contrib-watch'); + + grunt.registerTask('default', [ + 'csslint', + 'eslint', + 'concurrent:dev' + ]); + + grunt.registerTask('prod', [ + 'csslint', + 'eslint', + 'cssmin', + 'uglify' + ]); + }; +}(module, require)); diff --git a/www/lib/angular-tooltips/Readme.md b/www/lib/angular-tooltips/Readme.md new file mode 100644 index 00000000..cba42b0f --- /dev/null +++ b/www/lib/angular-tooltips/Readme.md @@ -0,0 +1,210 @@ +Angular Tooltips +================== + +[](https://gitter.im/720kb/angular-tooltips?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + + +Angular Tooltips is an angularjs directive that generates a tooltip on your element. + + +The angular tooltips is developed by [720kb](http://720kb.net). + +##Requirements + + +AngularJS v1.2+ + +##Screen + + +###Browser support + + +Chrome  + +Firefox  + +Safari  + +Opera  + +IE  + + +## Load + +To use the directive, include the angular tooltips's javascript and css files in your web page: + +```html +<!DOCTYPE HTML> +<html> +<head> + <link href="src/css/angular-tooltips.css" rel="stylesheet" type="text/css" /> +</head> +<body ng-app="app"> + //..... + <script src="src/js/angular-tooltips.js"></script> +</body> +</html> +``` + +##Install + +###Bower installation + +``` +$ bower install angular-tooltips --save +``` + +_then load the js files in your html_ + +###Add module dependency + +Add the 720kb.tooltips module dependency + +```js +angular.module('app', [ + '720kb.tooltips' + ]); +``` + + +Call the directive wherever you want in your html page + +```html + +<a href="#" tooltips title="tooltip">Tooltip me</a> + +``` +##Options +Angular tooltips allows you to use some options via `attribute` data + +####Tooltip position +You can set your tooltip to show on `left` or `right` or `top` or `bottom` position +using the `tooltip-side=""` attribute +```html +<a href="#" tooltips tooltip-title="tip" tooltip-side="top">Tooltip me</a> +<a href="#" tooltips tooltip-title="tip" tooltip-side="bottom">Tooltip me</a> +<a href="#" tooltips tooltip-title="tip" tooltip-side="left">Tooltip me</a> +<a href="#" tooltips tooltip-title="tip" tooltip-side="right">Tooltip me</a> +``` + +####Tooltip title +You can set your tooltip title (text/html doesn't matter) +using the `tooltip-title=""` attribute or simply via `title=""` html attribute + +```html +<a href="#" tooltips tooltip-title="tip" tooltip-title="Hey" tooltip-content="<i>Woa!</i>">Tooltip me</a> +<a href="#" tooltips tooltip-title="tip" title="Hey" tooltip-content="<i>Woa!</i>">Tooltip me</a> +``` + +####Tooltip content +You can set your tooltip content +using the `tooltip-content=""` attribute + +```html +<a href="#" tooltips tooltip-title="tip" tooltip-content="Woa!">Tooltip me</a> +``` + +####Tooltip HTML content +You can set your tooltip html content +using the `tooltip-html=""` attribute + +```html +<a href="#" tooltips tooltip-title="tip" tooltip-html="<i>Woa!</i>">Tooltip me</a> +``` + +####Tooltip size +You can set your tooltip size (small || medium || large) +using the `tooltip-size=""` attribute + +```html +<a href="#" tooltips tooltip-title="tip" tooltip-size="small">Tooltip me</a> +<a href="#" tooltips tooltip-title="tip" tooltip-size="medium">Tooltip me</a> +<a href="#" tooltips tooltip-title="tip" tooltip-size="large">Tooltip me</a> +``` +####Tooltip speed +You can set the tooltip transition speed ('fast' || 'medium' || 'slow' || int(milliseconds)) +using the `tooltip-speed=""` attribute + +```html +<a href="#" tooltips tooltip-speed="fast" tooltip-title="tip">Tooltip fast</a> +<a href="#" tooltips tooltip-speed="medium" tooltip-title="tip">Tooltip medium</a> +<a href="#" tooltips tooltip-speed="slow" tooltip-title="tip">Tooltip slow</a> +<a href="#" tooltips tooltip-speed="950" tooltip-title="tip">Tooltip custom</a> +``` +####Tooltip delay +You can set the tooltip transition delay (ms) +using the `tooltip-delay=""` attribute + +```html +<a href="#" tooltips tooltip-delay="800" tooltip-title="tip">Tooltip in 800ms</a> +``` +####Tooltip try +If space is not available for tooltip , it will automatically search for a similar alternative position to show. You can set tooltip try (1 || 0) +using the `tooltip-try=""` attribute + +```html +<a href="#" tooltips tooltip-title="tip" tooltip-try="1">Tooltip me</a> +<a href="#" tooltips tooltip-title="tip" tooltip-try="0">Tooltip me</a> +``` +####Tooltip lazy +If you don't want to re-init the tooltip position everytime the tooltip trigger events are fired, you can set tooltip lazy mode (true || false) +using the `tooltip-lazy=""` attribute + +```html +<a href="#" tooltips tooltip-lazy="false" tooltip-content="Hi" tooltip-show-trigger="mouseover"> +I will re-init my position everytime the mouseover event is fired +</a> +<a href="#" tooltips tooltip-lazy="true" tooltip-content="Hi" tooltip-show-trigger="mouseover"> +I will init my position on mouseover only the first time event is fired +</a> +``` + +####Tooltip triggers +You can set your tooltip to show/hide on specific event/events, you can use the `tooltip-show-trigger=""` and the `tooltip-hide-trigger=""` attribute for this scope +```html +<a href="#" tooltips tooltip-title="tip" tooltip-show-trigger="click" tooltip-side="top">Show tooltip only on click</a> +<a href="#" tooltips tooltip-title="tip" tooltip-hide-trigger="click" tooltip-side="bottom">Hide tooltip only on click</a> +<a href="#" tooltips tooltip-title="tip" tooltip-show-trigger="mouseover click" tooltip-hide-trigger="click" tooltip-side="left">Show tooltip on click and mouseover and hide tooltip only on click</a> +``` + +_**Close button**_ + +If you want to hide on click, you can configure a close button using text or HTML. This allows your users to click the button inside the tooltip instead of clicking on the original trigger. +```html +<a href="#" tooltips tooltip-title="tip" tooltip-show-trigger="mouseover click" tooltip-hide-trigger="click" tooltip-close-button="x" tooltip-side="left">Show tooltip on click and mouseover and hide tooltip only on click, with option to click on the X</a> +<a href="#" tooltips tooltip-title="tip" tooltip-show-trigger="mouseover click" tooltip-hide-trigger="click" tooltip-close-button='<button type="button">Close Me!</button>' tooltip-side="left">Show tooltip on click and mouseover and hide tooltip only on click, with option to click on HTML button</a> +``` + +####Tooltip CSS class +You can set a custom CSS class or a set of, using the `tooltip-class=""` attribute: +```html +<a href="#" tooltips tooltip-class="tooltip-custom tooltip-for-me" tooltip-title="tip" tooltip-side="top"> +I will show a tooltip with class="tooltip-custom tooltip-for-me" +</a> +``` + +## Example + +###[Live demo](https://720kb.github.io/angular-tooltips) + +##Theming +You can edit the default Css file `angular-tooltips.css` if you want to make a new theme for the tooltips. + +##Contributing + +We will be much grateful if you help us making this project to grow up. +Feel free to contribute by forking, opening issues, pull requests etc. + +## License + +The MIT License (MIT) + +Copyright (c) 2014 Filippo Oretti, Dario Andrei + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/www/lib/angular-tooltips/assets/js/index.js b/www/lib/angular-tooltips/assets/js/index.js new file mode 100644 index 00000000..d0809df3 --- /dev/null +++ b/www/lib/angular-tooltips/assets/js/index.js @@ -0,0 +1,21 @@ +/*global angular*/ + +(function withAngular(angular) { + 'use strict'; + + angular.module('720kb', [ + 'ngRoute', + '720kb.tooltips' + ]) + .controller('Ctrl', [ + '$scope', + '$timeout', + function ($scope, $timeout) { + + $scope.items = ['1','2','4','5']; + $timeout(function () { + $scope.items.push('7'); + $scope.items.push('9'); + }, 5000); + }]); +}(angular)); diff --git a/www/lib/angular-tooltips/bower.json b/www/lib/angular-tooltips/bower.json new file mode 100644 index 00000000..e6524656 --- /dev/null +++ b/www/lib/angular-tooltips/bower.json @@ -0,0 +1,27 @@ +{ + "name": "angular-tooltips", + "version": "0.1.14", + "description": "A tooltips directive for angularjs.", + "authors": [ + "Filippo Oretti <filippo.oretti@gmail.com", + "Dario Andrei <wouldgo84@gmail.com>" + ], + "keywords": [ + "tooltip", + "tooltips", + "angularjs" + ], + "main": [ + "./dist/angular-tooltips.min.js", + "./dist/angular-tooltips.min.css" + ], + "license": "MIT", + "homepage": "http://720kb.github.io/angular-tooltips", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} diff --git a/www/lib/angular-tooltips/config/csslintrc.json b/www/lib/angular-tooltips/config/csslintrc.json new file mode 100644 index 00000000..be56338d --- /dev/null +++ b/www/lib/angular-tooltips/config/csslintrc.json @@ -0,0 +1,37 @@ +{ + "important": true, + "adjoining-classes": false, + "known-properties": false, + "box-sizing": false, + "box-model": false, + "overqualified-elements": false, + "display-property-grouping": false, + "bulletproof-font-face": false, + "compatible-vendor-prefixes": true, + "regex-selectors": false, + "errors": true, + "duplicate-background-images": true, + "duplicate-properties": true, + "empty-rules": true, + "selector-max-approaching": false, + "gradients": true, + "fallback-colors": false, + "font-sizes": false, + "font-faces": false, + "floats": false, + "star-property-hack": false, + "outline-none": false, + "import": true, + "ids": false, + "underscore-property-hack": true, + "rules-count": true, + "qualified-headings": false, + "selector-max": false, + "shorthand": false, + "text-indent": false, + "unique-headings": false, + "universal-selector": false, + "unqualified-attributes": true, + "vendor-prefix": true, + "zero-units": true +} diff --git a/www/lib/angular-tooltips/config/eslint.json b/www/lib/angular-tooltips/config/eslint.json new file mode 100644 index 00000000..27b3ca70 --- /dev/null +++ b/www/lib/angular-tooltips/config/eslint.json @@ -0,0 +1,153 @@ +//http://eslint.org/docs/rules/ +{ + "rules": { + "no-cond-assign": 2, + "no-constant-condition": 2, + "no-comma-dangle": 2, + "no-control-regex": 2, + "no-debugger": 2, + "no-dupe-keys": 2, + "no-empty": 2, + "no-extra-semi": 2, + "no-inner-declarations": 2, + "no-invalid-regexp": 2, + "no-negated-in-lhs": 2, + "no-obj-calls": 2, + "no-regex-spaces": 2, + "no-sparse-arrays": 2, + "no-unreachable": 2, + "use-isnan": 2, + "valid-typeof": 2, + "camelcase": 2, + "eqeqeq": 2, + "no-plusplus": 2, + "no-bitwise": 2, + "block-scoped-var": 2, + "consistent-return": 2, + "curly": [ + 2, + "all" + ], + "default-case": 2, + "dot-notation": 2, + "no-caller": 2, + "no-div-regex": 2, + "no-else-return": 2, + "no-empty-label": 2, + "no-eq-null": 2, + "no-eval": 2, + "no-extend-native": 2, + "no-fallthrough": 2, + "no-floating-decimal": 2, + "no-implied-eval": 2, + "no-labels": 2, + "no-iterator": 2, + "no-lone-blocks": 2, + "no-loop-func": 2, + "no-multi-str": 2, + "no-native-reassign": 2, + "no-new": 2, + "no-new-func": 2, + "no-new-wrappers": 2, + "no-octal": 2, + "no-octal-escape": 2, + "no-proto": 2, + "no-redeclare": 2, + "no-return-assign": 2, + "no-script-url": 2, + "no-self-compare": 2, + "no-sequences": 2, + "no-unused-expressions": 2, + "no-with": 2, + "yoda": 2, + "radix": 2, + "wrap-iife": [ + 2, + "outside" + ], + "global-strict": [2, "never"], + "no-extra-strict": 2, + "strict": 2, + "no-catch-shadow": 2, + "no-delete-var": 2, + "no-label-var": 2, + "no-shadow": 2, + "no-shadow-restricted-names": 2, + "no-undef": 2, + "no-undef-init": 2, + "no-unused-vars": [ + 2, + { + "vars": "all", + "args": "after-used" + } + ], + "no-use-before-define": 2, + "brace-style": [ + 2, + "1tbs" + ], + "consistent-this": [ + 2, + "that" + ], + "new-cap": 2, + "new-parens": 2, + "no-nested-ternary": 2, + "no-array-constructor": 2, + "no-lonely-if": 2, + "no-new-object": 2, + "no-spaced-func": 2, + "no-space-before-semi": 2, + "no-underscore-dangle": 2, + "no-wrap-func": 2, + "quotes": [ + 2, + "single", + "avoid-escape" + ], + "quote-props": 2, + "semi": [ + 2, + "always" + ], + "space-after-keywords": [ + 2, + "always" + ], + "space-in-brackets": [ + 2, + "never" + ], + "space-infix-ops": 2, + "space-return-throw-case": 2, + "space-unary-ops": 2, + "one-var": 2, + "wrap-regex": 2, + + "no-extra-boolean-cast": 1, + "no-console": 1, + "no-alert": 1, + "no-empty-class": 1, + "no-ex-assign": 1, + "no-func-assign": 1, + "valid-jsdoc": 1, + "guard-for-in": 1, + "no-warning-comments": [ + 1, + { + "terms": ["todo", "fixme", "xxx"], + "location": "anywhere" + } + ], + "func-style": [ + 1, + "expression" + ], + "no-extra-parens": 1, + "func-names": 1, + + "no-ternary": 0, + "sort-vars": 0 + } +} diff --git a/www/lib/angular-tooltips/dist/angular-tooltips.min.css b/www/lib/angular-tooltips/dist/angular-tooltips.min.css new file mode 100644 index 00000000..aeaf5ef9 --- /dev/null +++ b/www/lib/angular-tooltips/dist/angular-tooltips.min.css @@ -0,0 +1 @@ +._720kb-tooltip{background:rgba(0,0,0,.8);color:#fff;position:absolute;z-index:9;padding:.4% 1%;opacity:0;visibility:hidden;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;left:-200%;top:0}._720kb-tooltip-title{color:rgba(255,255,255,.95);font-weight:500;width:100%;clear:both}._720kb-tooltip-caret:after,._720kb-tooltip-caret:before{content:'';position:absolute;width:0;height:0}._720kb-tooltip._720kb-tooltip-small{padding:4.5px 10px;font-size:12px}._720kb-tooltip._720kb-tooltip-medium{padding:7px 15px;font-size:13.5px}._720kb-tooltip._720kb-tooltip-large{padding:10px 20px;font-size:14px}._720kb-tooltip._720kb-tooltip-open{visibility:visible;visibility:initial;opacity:1}._720kb-tooltip-caret:before{border:6px solid rgba(0,0,0,.8)}._720kb-tooltip-caret:after{border:5px solid transparent}._720kb-tooltip-left ._720kb-tooltip-caret:after,._720kb-tooltip-left ._720kb-tooltip-caret:before,._720kb-tooltip-right ._720kb-tooltip-caret:after,._720kb-tooltip-right ._720kb-tooltip-caret:before{top:50%;border-top-color:transparent;border-bottom-color:transparent}._720kb-tooltip-left ._720kb-tooltip-caret:before{left:100%;margin-left:0;margin-top:-6px;border-right-width:0}._720kb-tooltip-left ._720kb-tooltip-caret:after{left:100%;margin-left:0;margin-top:-5px;border-right-width:0}._720kb-tooltip-right ._720kb-tooltip-caret:before{left:0;margin-left:-6px;margin-top:-6px;border-left-width:0}._720kb-tooltip-right ._720kb-tooltip-caret:after{left:0;margin-left:-5px;margin-top:-5px;border-left-width:0}._720kb-tooltip-top ._720kb-tooltip-caret:after,._720kb-tooltip-top ._720kb-tooltip-caret:before{left:50%;border-right-color:transparent;border-left-color:transparent;top:100%;border-bottom-width:0}._720kb-tooltip-top ._720kb-tooltip-caret:before{margin-left:-6px;margin-bottom:-6px}._720kb-tooltip-top ._720kb-tooltip-caret:after{margin-left:-5px;margin-bottom:-5px}._720kb-tooltip-bottom ._720kb-tooltip-caret:before{bottom:100%;left:50%;margin-left:-6px;border-right-color:transparent;border-left-color:transparent;border-top-width:0}._720kb-tooltip-bottom ._720kb-tooltip-caret:after{bottom:100%;left:50%;margin-left:-5px;border-right-color:transparent;border-left-color:transparent;border-top-width:0}._720kb-tooltip-close-button{float:right} diff --git a/www/lib/angular-tooltips/dist/angular-tooltips.min.js b/www/lib/angular-tooltips/dist/angular-tooltips.min.js new file mode 100644 index 00000000..53f288fb --- /dev/null +++ b/www/lib/angular-tooltips/dist/angular-tooltips.min.js @@ -0,0 +1,14 @@ +/*! + * Angular Tooltips v0.1.14 + * + * Released under the MIT license + * www.opensource.org/licenses/MIT + * + * Brought to you by 720kb.net + * + * 2015-07-14 + */ + + +!function(a){"use strict";a.module("720kb.tooltips",[]).directive("tooltips",["$window","$compile","$interpolate",function(b,c,d){var e=8,f=9,g=10,h="_720kb-tooltip-",i=d.startSymbol(),j=d.endSymbol();return{restrict:"A",scope:{},link:function(d,k,l){function m(){M&&x||(x=!0,d.initTooltip(G)),I&&d.tooltipTryPosition(),d.showTooltip()}function n(){d.hideTooltip()}function o(){d.hideTooltip(),d.initTooltip(F)}var p,q,r,s,t,u,v,w,x=!1,y=a.element(k[0]),z=a.element(b.document.getElementsByTagName("body")[0]),A=l.tooltipTitle||l.title||"",B=l.tooltipContent||"",C=l.tooltipHtml||"",D=l.tooltipShowTrigger||"mouseover",E=l.tooltipHideTrigger||"mouseleave",F=l.tooltipSide||"top",G=F,H=l.tooltipSize||"medium",I="undefined"!=typeof l.tooltipTry&&null!==l.tooltipTry?d.$eval(l.tooltipTry):!0,J=l.tooltipClass||"",K=(l.tooltipSpeed||"medium").toLowerCase(),L=l.tooltipDelay||0,M="undefined"!=typeof l.tooltipLazy&&null!==l.tooltipLazy?d.$eval(l.tooltipLazy):!0,N="undefined"!=typeof l.tooltipCloseButton&&null!==l.tooltipCloseButton,O=l.tooltipCloseButton||"",P='<div class="_720kb-tooltip '+h+H+'">';N&&(P=P+'<span class="'+h+'close-button" ng-click="hideTooltip()"> '+O+" </span>"),l.tooltipView&&(P=l.tooltipViewCtrl?P+'<div ng-controller="'+l.tooltipViewCtrl+'" ng-include="\''+l.tooltipView+"'\"></div>":P+"<div ng-include=\"'"+l.tooltipView+"'\"></div>"),P=P+'<div class="'+h+'title"> '+i+"title"+j+"</div>"+i+"content"+j+C+' <span class="'+h+'caret"></span></div>',d.title=A,d.content=B,d.html=C,d.parseSpeed=function(){switch(K){case"fast":K=100;break;case"medium":K=450;break;case"slow":K=800;break;default:K=Number(K)}},p=c(P)(d),p.addClass(J),z.append(p),d.isTooltipEmpty=function(){return d.title||d.content||d.html?void 0:!0},d.initTooltip=function(a){d.isTooltipEmpty()?p.css("visibility","hidden"):(p.css("visibility",""),t=y[0].offsetHeight,u=y[0].offsetWidth,v=d.getRootOffsetTop(y[0],0),w=d.getRootOffsetLeft(y[0],0),q=p[0].offsetHeight,r=p[0].offsetWidth,d.parseSpeed(),d.tooltipPositioning(a))},d.getRootOffsetTop=function(a,b){return a.offsetParent?d.getRootOffsetTop(a.offsetParent,b+a.offsetTop-a.scrollTop):b+a.offsetTop},d.getRootOffsetLeft=function(a,b){return a.offsetParent?d.getRootOffsetLeft(a.offsetParent,b+a.offsetLeft-a.scrollLeft):b+a.offsetLeft},d.bindShowTriggers=function(){y.bind(D,m)},d.bindHideTriggers=function(){y.bind(E,n)},d.clearTriggers=function(){y.unbind(D,m),y.unbind(E,n)},d.bindShowTriggers(),d.showTooltip=function(){p.addClass(h+"open"),p.css("transition","opacity "+K+"ms linear"),L&&p.css("transition-delay",L+"ms"),d.clearTriggers(),d.bindHideTriggers()},d.hideTooltip=function(){p.removeClass(h+"open"),p.css("transition",""),d.clearTriggers(),d.bindShowTriggers()},d.removePosition=function(){p.removeClass(h+"left").removeClass(h+"right").removeClass(h+"top").removeClass(h+"bottom ")},d.tooltipPositioning=function(a){d.removePosition();var b,c;"small"===H?s=e:"medium"===H?s=f:"large"===H&&(s=g),"left"===a&&(b=v+t/2-q/2,c=w-(r+s),p.css("top",b+"px"),p.css("left",c+"px"),p.addClass(h+"left")),"right"===a&&(b=v+t/2-q/2,c=w+u+s,p.css("top",b+"px"),p.css("left",c+"px"),p.addClass(h+"right")),"top"===a&&(b=v-s-q,c=w+u/2-r/2,p.css("top",b+"px"),p.css("left",c+"px"),p.addClass(h+"top")),"bottom"===a&&(b=v+t+s,c=w+u/2-r/2,p.css("top",b+"px"),p.css("left",c+"px"),p.addClass(h+"bottom"))},d.tooltipTryPosition=function(){var a=p[0].offsetHeight,c=p[0].offsetWidth,e=p[0].offsetTop,f=p[0].offsetLeft,g=b.outerWidth,h=b.outerHeight,i=g-(c+f),j=h-(a+e),k=y[0].offsetHeight,l=y[0].offsetWidth,m=y[0].offsetLeft,n=y[0].offsetTop,o=g-(m+l),q=h-(k+n),r={left:f,top:e,bottom:j,right:i},s={left:m,right:o,top:n,bottom:q},t=Object.keys(s).reduce(function(a,b){return s[a]>s[b]?a:b}),u=Object.keys(r).reduce(function(a,b){return r[a]<r[b]?a:b});F!==t&&r[u]<20&&(G=t,d.tooltipPositioning(G),d.initTooltip(t))},a.element(b).bind("resize",o),d.$on("$destroy",function(){a.element(b).unbind("resize",o),d.clearTriggers(),p.remove()}),l.tooltipTitle&&l.$observe("tooltipTitle",function(a){d.title=a,d.initTooltip(G)}),l.title&&l.$observe("title",function(a){d.title=a,d.initTooltip(G)}),l.tooltipContent&&l.$observe("tooltipContent",function(a){d.content=a,d.initTooltip(G)}),l.tooltipHtml&&l.$observe("tooltipHtml",function(a){d.html=a,d.initTooltip(G)})}}}])}(angular); +//# sourceMappingURL=angular-tooltips.sourcemap.map diff --git a/www/lib/angular-tooltips/dist/angular-tooltips.sourcemap.map b/www/lib/angular-tooltips/dist/angular-tooltips.sourcemap.map new file mode 100644 index 00000000..283dcb4f --- /dev/null +++ b/www/lib/angular-tooltips/dist/angular-tooltips.sourcemap.map @@ -0,0 +1 @@ +{"version":3,"file":"angular-tooltips.min.js","sources":["../src/js/angular-tooltips.js"],"names":["angular","module","directive","$window","$compile","$interpolate","TOOLTIP_SMALL_MARGIN","TOOLTIP_MEDIUM_MARGIN","TOOLTIP_LARGE_MARGIN","CSS_PREFIX","INTERPOLATE_START_SYM","startSymbol","INTERPOLATE_END_SYM","endSymbol","restrict","scope","link","$scope","element","attr","onMouseEnterAndMouseOver","lazyMode","initialized","initTooltip","side","tryPosition","tooltipTryPosition","showTooltip","onMouseLeaveAndMouseOut","hideTooltip","onResize","originSide","theTooltip","theTooltipHeight","theTooltipWidth","theTooltipMargin","height","width","offsetTop","offsetLeft","thisElement","body","document","getElementsByTagName","title","tooltipTitle","content","tooltipContent","html","tooltipHtml","showTriggers","tooltipShowTrigger","hideTriggers","tooltipHideTrigger","tooltipSide","size","tooltipSize","tooltipTry","$eval","className","tooltipClass","speed","tooltipSpeed","toLowerCase","delay","tooltipDelay","tooltipLazy","hasCloseButton","tooltipCloseButton","closeButtonContent","htmlTemplate","tooltipView","tooltipViewCtrl","parseSpeed","Number","addClass","append","isTooltipEmpty","css","offsetHeight","offsetWidth","getRootOffsetTop","getRootOffsetLeft","tooltipPositioning","elem","val","offsetParent","scrollTop","scrollLeft","bindShowTriggers","bind","bindHideTriggers","clearTriggers","unbind","removeClass","removePosition","topValue","leftValue","theTooltipH","theTooltipW","topOffset","leftOffset","winWidth","outerWidth","winHeight","outerHeight","rightOffset","bottomOffset","elmHeight","elmWidth","elmOffsetLeft","elmOffsetTop","elmOffsetRight","elmOffsetBottom","offsets","left","top","bottom","right","posix","bestPosition","Object","keys","reduce","best","key","worstOffset","worst","$on","remove","$observe"],"mappings":"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"}
\ No newline at end of file diff --git a/www/lib/angular-tooltips/index.html b/www/lib/angular-tooltips/index.html new file mode 100644 index 00000000..68344145 --- /dev/null +++ b/www/lib/angular-tooltips/index.html @@ -0,0 +1,122 @@ +<!doctype html> +<html ng-app="720kb"> +<head> + <link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.1.0/css/font-awesome.min.css"> + <link rel="stylesheet" type="text/css" href="http://720kb.github.io/csshelper/assets/ext/src/helper.css"> + <link rel="stylesheet" type="text/css" href="src/css/angular-tooltips.css"> + <title>Angularjs Tooltips</title> +</head> +<body class="center-content"> + <div class="separator100"></div> + + <div class="col6 offset-left2"> + <div> + <a class="btn btn-medium bg-info color-white font-bold" + tooltip-delay="400" + tooltips + tooltip-content="Yeo man!" + tooltip-view="views/index.html" + tooltip-view-ctrl="Ctrl" + tooltip-size="small" + tooltip-side="left" + tooltip-title="hey!" > + Small left + </a> + </div> + <div class="separator30"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-hide-trigger="click" tooltip-size="medium" tooltip-side="left" title="Title attr" > + Medium left + </a> + </div> + + <div class="separator30"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-size="large" tooltip-side="left" > + Large left + </a> + </div> + + <div class="separator100"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-size="small" tooltip-side="right" > + Small right + </a> + </div> + + <div class="separator30"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-size="medium" tooltip-side="right" > + Medium right + </a> + </div> + + <div class="separator30"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-size="large" tooltip-side="right" > + Large right + </a> + </div> + <div class="separator100"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-size="small" tooltip-side="top" > + Small top + </a> + </div> + + <div class="separator30"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-size="medium" tooltip-side="top" > + Medium top + </a> + </div> + + <div class="separator30"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-size="large" tooltip-side="top" > + Large top + </a> + </div> + <div class="separator100"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-size="small" tooltip-side="bottom" > + Small bottom + </a> + </div> + + <div class="separator30"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-size="medium" tooltip-side="bottom" > + Medium bottom + </a> + </div> + + <div class="separator30"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-size="large" tooltip-side="bottom" > + Large bottom + </a> + </div> + <div class="separator100"></div> + + <div class="separator30"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-show-trigger="mouseover click" tooltip-hide-trigger="click" tooltip-close-button='<button type="button">Close Me!</button>' tooltip-side="top"> + Close button: hover to open, click to close + </a> + </div> + + <div class="separator30"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man! I have some more to say to show the positioning of the close button" tooltip-show-trigger="click" tooltip-hide-trigger="click" tooltip-close-button='<button type="button">Close Me!</button>' tooltip-side="bottom"> + Close button: click to open and close + </a> + </div> + <div class="separator100"></div> +</div> +<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.4/angular.min.js"></script> +<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.4/angular-route.min.js"></script> +<script type="text/javascript" src="src/js/angular-tooltips.js"></script> +<script type="text/javascript" src="assets/js/index.js"></script> +</body> +</html> diff --git a/www/lib/angular-tooltips/package.json b/www/lib/angular-tooltips/package.json new file mode 100644 index 00000000..4c0fb2d0 --- /dev/null +++ b/www/lib/angular-tooltips/package.json @@ -0,0 +1,40 @@ +{ + "name": "angular-tooltips", + "version": "0.1.14", + "description": "A tooltips directive for angularjs.", + "homepage": "http://720kb.github.io/angular-tooltips", + "main": "dist/angular-tooltips.min.js", + "keywords": [ + "date", + "tooltips", + "input" + ], + "repository": { + "type": "git", + "url": "https://github.com/720kb/angular-tooltips.git" + }, + "license": "MIT", + "devDependencies": { + "connect-modrewrite": "*", + "grunt": "*", + "grunt-concurrent": "*", + "grunt-contrib-connect": "*", + "grunt-contrib-csslint": "*", + "grunt-contrib-cssmin": "*", + "grunt-contrib-uglify": "*", + "grunt-contrib-watch": "*", + "grunt-eslint": "*" + }, + "author": { + "name": "Filippo Oretti", + "email": "filippo.oretti@gmail.com", + "url": "https://github.com/45kb" + }, + "contributors": [ + { + "name": "Dario Andrei", + "email": "wouldgo84@gmail.com", + "url": "https://github.com/wouldgo" + } + ] +} diff --git a/www/lib/angular-tooltips/semantic-ui.html b/www/lib/angular-tooltips/semantic-ui.html new file mode 100644 index 00000000..52eb1825 --- /dev/null +++ b/www/lib/angular-tooltips/semantic-ui.html @@ -0,0 +1,120 @@ +<!doctype html> +<html ng-app="720kb"> +<head> + <link rel="stylesheet" type="text/css" href="http://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.1.0/css/font-awesome.min.css"> + <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/1.5.2/semantic.min.css"> + <link rel="stylesheet" type="text/css" href="src/css/angular-tooltips.css"> + <title>Semantic-UI Tooltips</title> +</head> +<body class="center-content" style="margin:50px auto; float:none; max-width:200px;"> + <div class="ui divider"></div> + <div class="col6 offset-left2"> + <div class="ui button" tooltips tooltip-content="Yeo man!" tooltip-size="small" tooltip-side="left"> + Small left + </div> + <div class="ui divider"></div> + + <div class="ui teal button" tooltips tooltip-content="Yeo man!" tooltip-size="medium" tooltip-side="left"> + Medium left + </div> + + <div class="ui divider"></div> + <div class="ui green button" tooltips tooltip-content="Yeo man!" tooltip-size="large" tooltip-side="left"> + Large left + </div> + + <div class="ui divider"></div> + <div class="ui button" tooltips tooltip-content="Yeo man!" tooltip-size="small" tooltip-side="right"> + Small right + </div> + <div class="ui divider"></div> + + <div class="ui teal button" tooltips tooltip-content="Yeo man!" tooltip-size="medium" tooltip-side="right"> + Medium right + </div> + + <div class="ui divider"></div> + + <div class="ui green button" tooltips tooltip-content="Yeo man!" tooltip-size="large" tooltip-side="right"> + Large right + </div> + + + + <div class="ui divider"></div> + <div class="ui button" tooltips tooltip-content="Yeo man!" tooltip-size="small" tooltip-side="top"> + Small top + </div> + <div class="ui divider"></div> + + <div class="ui teal button" tooltips tooltip-content="Yeo man!" tooltip-size="medium" tooltip-side="top"> + Medium top + </div> + + <div class="ui divider"></div> + <div class="ui green button" tooltips tooltip-content="Yeo man!" tooltip-size="large" tooltip-side="top"> + Large top + </div> + + <div class="ui divider"></div> + <div class="ui button" tooltips tooltip-content="Yeo man!" tooltip-size="small" tooltip-side="bottom"> + Small bottom + </div> + <div class="ui divider"></div> + + <div class="ui teal button" tooltips tooltip-content="Yeo man!" tooltip-size="medium" tooltip-side="bottom"> + Medium bottom + </div> + + <div class="ui divider"></div> + <div class="ui green button" tooltips tooltip-content="Yeo man!" tooltip-size="large" tooltip-side="bottom"> + Large bottom + </div> + <div class="ui divider"></div> + </div> + + <table class="ui small sortable padded table segment"> + <thead> + <tr> + <th class="two wide">pos</th> + <th class="three wide">val1</th> + <th class="one wide">val2</th> + <th class="five wide">val3</th> + </tr> + </thead> + <tbody> + <tr> + <td><a tooltips tooltip-content="Yeo man!" tooltip-side="top">top</a></td> + <td>111</td> + <td>222</td> + <td>333</td> + </tr> + <tr> + <td><a tooltips tooltip-content="Yeo man!" tooltip-side="bottom">bottom</a></td> + <td>111</td> + <td>222</td> + <td>333</td> + </tr> + <tr> + <td><a tooltips tooltip-content="Yeo man!" tooltip-side="left">left</a></td> + <td>111</td> + <td>222</td> + <td>333</td> + </tr> + <tr> + <td><a tooltips tooltip-content="Yeo man!" tooltip-side="right">right</a></td> + <td>111</td> + <td>222</td> + <td>333</td> + </tr> + </tbody> + </table> + + <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> + <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.4/angular.min.js"></script> + <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.4/angular-route.min.js"></script> + <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/1.5.2/semantic.min.js"></script> + <script type="text/javascript" src="src/js/angular-tooltips.js"></script> + <script type="text/javascript" src="assets/js/index.js"></script> +</body> +</html> diff --git a/www/lib/angular-tooltips/src/css/angular-tooltips.css b/www/lib/angular-tooltips/src/css/angular-tooltips.css new file mode 100644 index 00000000..6d3f4712 --- /dev/null +++ b/www/lib/angular-tooltips/src/css/angular-tooltips.css @@ -0,0 +1,124 @@ +._720kb-tooltip { + background: rgba(0, 0, 0, 0.8); + color:white; + position:absolute; + z-index:9; + padding:0.4% 1%; + opacity:0; + visibility: hidden; + -webkit-border-radius:3px; + -moz-border-radius:3px; + border-radius:3px; + left: -200%; + top: 0; +} +._720kb-tooltip-title{ + color:rgba(255,255,255,0.95); + font-weight: 500; + width: 100%; + clear:both; +} +._720kb-tooltip._720kb-tooltip-small { + padding:4.5px 10px; + font-size: 12px; +} +._720kb-tooltip._720kb-tooltip-medium { + padding:7px 15px; + font-size: 13.5px; +} +._720kb-tooltip._720kb-tooltip-large { + padding:10px 20px; + font-size: 14px; +} +._720kb-tooltip._720kb-tooltip-open { + visibility: visible; + visibility: initial; + opacity: 1; +} +._720kb-tooltip-caret:before { + content:''; + position: absolute; + width: 0; + height: 0; + border: 6px solid rgba(0, 0, 0, 0.8); +} +._720kb-tooltip-caret:after { + content:''; + position: absolute; + width: 0; + height: 0; + border: 5px solid transparent; +} +._720kb-tooltip-left ._720kb-tooltip-caret:before { + top: 50%; + left: 100%; + margin-left:0; + margin-top: -6px; + border-top-color: transparent; + border-bottom-color: transparent; + border-right-width: 0; +} +._720kb-tooltip-left ._720kb-tooltip-caret:after { + top: 50%; + left: 100%; + margin-left:0; + margin-top: -5px; + border-top-color: transparent; + border-bottom-color: transparent; + border-right-width: 0; +} +._720kb-tooltip-right ._720kb-tooltip-caret:before { + top: 50%; + left:0; + margin-left:-6px; + margin-top: -6px; + border-top-color: transparent; + border-bottom-color: transparent; + border-left-width: 0; +} +._720kb-tooltip-right ._720kb-tooltip-caret:after { + top: 50%; + left:0; + margin-left:-5px; + margin-top: -5px; + border-top-color: transparent; + border-bottom-color: transparent; + border-left-width: 0; +} +._720kb-tooltip-top ._720kb-tooltip-caret:before { + top: 100%; + left: 50%; + margin-left: -6px; + margin-bottom:-6px; + border-right-color: transparent; + border-left-color: transparent; + border-bottom-width: 0; +} +._720kb-tooltip-top ._720kb-tooltip-caret:after { + top: 100%; + left: 50%; + margin-left: -5px; + margin-bottom:-5px; + border-right-color: transparent; + border-left-color: transparent; + border-bottom-width: 0; +} +._720kb-tooltip-bottom ._720kb-tooltip-caret:before { + bottom: 100%; + left: 50%; + margin-left: -6px; + border-right-color: transparent; + border-left-color: transparent; + border-top-width: 0; +} +._720kb-tooltip-bottom ._720kb-tooltip-caret:after { + bottom: 100%; + left: 50%; + margin-left: -5px; + border-right-color: transparent; + border-left-color: transparent; + border-top-width: 0; +} +._720kb-tooltip-close-button { + float: right; +} diff --git a/www/lib/angular-tooltips/src/js/angular-tooltips.js b/www/lib/angular-tooltips/src/js/angular-tooltips.js new file mode 100644 index 00000000..5ad62199 --- /dev/null +++ b/www/lib/angular-tooltips/src/js/angular-tooltips.js @@ -0,0 +1,357 @@ +/*global angular*/ + +(function withAngular(angular) { + 'use strict'; + + angular.module('720kb.tooltips', []) + .directive('tooltips', ['$window', '$compile', '$interpolate', + function manageDirective($window, $compile, $interpolate) { + + var TOOLTIP_SMALL_MARGIN = 8 //px + , TOOLTIP_MEDIUM_MARGIN = 9 //px + , TOOLTIP_LARGE_MARGIN = 10 //px + , CSS_PREFIX = '_720kb-tooltip-' + , INTERPOLATE_START_SYM = $interpolate.startSymbol() + , INTERPOLATE_END_SYM = $interpolate.endSymbol(); + return { + 'restrict': 'A', + 'scope': {}, + 'link': function linkingFunction($scope, element, attr) { + + var initialized = false + , thisElement = angular.element(element[0]) + , body = angular.element($window.document.getElementsByTagName('body')[0]) + , theTooltip + , theTooltipHeight + , theTooltipWidth + , theTooltipMargin //used both for margin top left right bottom + , height + , width + , offsetTop + , offsetLeft + , title = attr.tooltipTitle || attr.title || '' + , content = attr.tooltipContent || '' + , html = attr.tooltipHtml || '' + , showTriggers = attr.tooltipShowTrigger || 'mouseover' + , hideTriggers = attr.tooltipHideTrigger || 'mouseleave' + , originSide = attr.tooltipSide || 'top' + , side = originSide + , size = attr.tooltipSize || 'medium' + , tryPosition = typeof attr.tooltipTry !== 'undefined' && attr.tooltipTry !== null ? $scope.$eval(attr.tooltipTry) : true + , className = attr.tooltipClass || '' + , speed = (attr.tooltipSpeed || 'medium').toLowerCase() + , delay = attr.tooltipDelay || 0 + , lazyMode = typeof attr.tooltipLazy !== 'undefined' && attr.tooltipLazy !== null ? $scope.$eval(attr.tooltipLazy) : true + , hasCloseButton = typeof attr.tooltipCloseButton !== 'undefined' && attr.tooltipCloseButton !== null + , closeButtonContent = attr.tooltipCloseButton || '' + , htmlTemplate = '<div class="_720kb-tooltip ' + CSS_PREFIX + size + '">'; + + if (hasCloseButton) { + + htmlTemplate = htmlTemplate + '<span class="' + CSS_PREFIX + 'close-button" ng-click="hideTooltip()"> ' + closeButtonContent + ' </span>'; + } + if (attr.tooltipView) { + if (attr.tooltipViewCtrl) { + + htmlTemplate = htmlTemplate + '<div ng-controller="' + attr.tooltipViewCtrl + '" ng-include="\'' + attr.tooltipView + '\'"></div>'; + } else { + + htmlTemplate = htmlTemplate + '<div ng-include="\'' + attr.tooltipView + '\'"></div>'; + } + } + + + htmlTemplate = htmlTemplate + '<div class="' + CSS_PREFIX + 'title"> ' + INTERPOLATE_START_SYM + 'title' + INTERPOLATE_END_SYM + '</div>' + + INTERPOLATE_START_SYM + 'content' + INTERPOLATE_END_SYM + html + ' <span class="' + CSS_PREFIX + 'caret"></span>' + + '</div>'; + + $scope.title = title; + $scope.content = content; + $scope.html = html; + //parse the animation speed of tooltips + $scope.parseSpeed = function parseSpeed () { + + switch (speed) { + case 'fast': + speed = 100; + break; + case 'medium': + speed = 450; + break; + case 'slow': + speed = 800; + break; + default: + speed = Number(speed); + } + }; + //create the tooltip + theTooltip = $compile(htmlTemplate)($scope); + + theTooltip.addClass(className); + + body.append(theTooltip); + + $scope.isTooltipEmpty = function checkEmptyTooltip () { + + if (!$scope.title && !$scope.content && !$scope.html) { + + return true; + } + }; + + $scope.initTooltip = function initTooltip (tooltipSide) { + if (!$scope.isTooltipEmpty()) { + theTooltip.css('visibility', ''); + + height = thisElement[0].offsetHeight; + width = thisElement[0].offsetWidth; + offsetTop = $scope.getRootOffsetTop(thisElement[0], 0); + offsetLeft = $scope.getRootOffsetLeft(thisElement[0], 0); + //get tooltip dimension + theTooltipHeight = theTooltip[0].offsetHeight; + theTooltipWidth = theTooltip[0].offsetWidth; + + $scope.parseSpeed(); + $scope.tooltipPositioning(tooltipSide); + } else { + theTooltip.css('visibility', 'hidden'); + } + }; + + $scope.getRootOffsetTop = function getRootOffsetTop (elem, val){ + + if (!elem.offsetParent){ + + return val + elem.offsetTop; + } + + return $scope.getRootOffsetTop(elem.offsetParent, val + elem.offsetTop - elem.scrollTop); + }; + + $scope.getRootOffsetLeft = function getRootOffsetLeft (elem, val){ + + if (!elem.offsetParent){ + + return val + elem.offsetLeft; + } + + return $scope.getRootOffsetLeft(elem.offsetParent, val + elem.offsetLeft - elem.scrollLeft); + }; + + function onMouseEnterAndMouseOver() { + if (!lazyMode || !initialized) { + + initialized = true; + $scope.initTooltip(side); + } + if (tryPosition) { + + $scope.tooltipTryPosition(); + } + $scope.showTooltip(); + } + + $scope.bindShowTriggers = function() { + thisElement.bind(showTriggers, onMouseEnterAndMouseOver); + }; + + function onMouseLeaveAndMouseOut() { + $scope.hideTooltip(); + } + + $scope.bindHideTriggers = function() { + thisElement.bind(hideTriggers, onMouseLeaveAndMouseOut); + }; + + $scope.clearTriggers = function() { + thisElement.unbind(showTriggers, onMouseEnterAndMouseOver); + thisElement.unbind(hideTriggers, onMouseLeaveAndMouseOut); + }; + + $scope.bindShowTriggers(); + + $scope.showTooltip = function showTooltip () { + theTooltip.addClass(CSS_PREFIX + 'open'); + theTooltip.css('transition', 'opacity ' + speed + 'ms linear'); + + if (delay) { + + theTooltip.css('transition-delay', delay + 'ms' ); + + } + + $scope.clearTriggers(); + $scope.bindHideTriggers(); + }; + + $scope.hideTooltip = function hideTooltip () { + theTooltip.removeClass(CSS_PREFIX + 'open'); + theTooltip.css('transition', ''); + $scope.clearTriggers(); + $scope.bindShowTriggers(); + }; + + $scope.removePosition = function removeTooltipPosition () { + + theTooltip + .removeClass(CSS_PREFIX + 'left') + .removeClass(CSS_PREFIX + 'right') + .removeClass(CSS_PREFIX + 'top') + .removeClass(CSS_PREFIX + 'bottom '); + }; + + $scope.tooltipPositioning = function tooltipPositioning (tooltipSide) { + + $scope.removePosition(); + + var topValue + , leftValue; + + if (size === 'small') { + + theTooltipMargin = TOOLTIP_SMALL_MARGIN; + + } else if (size === 'medium') { + + theTooltipMargin = TOOLTIP_MEDIUM_MARGIN; + + } else if (size === 'large') { + + theTooltipMargin = TOOLTIP_LARGE_MARGIN; + } + + if (tooltipSide === 'left') { + + topValue = offsetTop + height / 2 - theTooltipHeight / 2; + leftValue = offsetLeft - (theTooltipWidth + theTooltipMargin); + + theTooltip.css('top', topValue + 'px'); + theTooltip.css('left', leftValue + 'px'); + theTooltip.addClass(CSS_PREFIX + 'left'); + } + + if (tooltipSide === 'right') { + + topValue = offsetTop + height / 2 - theTooltipHeight / 2; + leftValue = offsetLeft + width + theTooltipMargin; + + theTooltip.css('top', topValue + 'px'); + theTooltip.css('left', leftValue + 'px'); + theTooltip.addClass(CSS_PREFIX + 'right'); + } + + if (tooltipSide === 'top') { + + topValue = offsetTop - theTooltipMargin - theTooltipHeight; + leftValue = offsetLeft + width / 2 - theTooltipWidth / 2; + + theTooltip.css('top', topValue + 'px'); + theTooltip.css('left', leftValue + 'px'); + theTooltip.addClass(CSS_PREFIX + 'top'); + } + + if (tooltipSide === 'bottom') { + + topValue = offsetTop + height + theTooltipMargin; + leftValue = offsetLeft + width / 2 - theTooltipWidth / 2; + theTooltip.css('top', topValue + 'px'); + theTooltip.css('left', leftValue + 'px'); + theTooltip.addClass(CSS_PREFIX + 'bottom'); + } + }; + + $scope.tooltipTryPosition = function tooltipTryPosition () { + + + var theTooltipH = theTooltip[0].offsetHeight + , theTooltipW = theTooltip[0].offsetWidth + , topOffset = theTooltip[0].offsetTop + , leftOffset = theTooltip[0].offsetLeft + , winWidth = $window.outerWidth + , winHeight = $window.outerHeight + , rightOffset = winWidth - (theTooltipW + leftOffset) + , bottomOffset = winHeight - (theTooltipH + topOffset) + //element OFFSETS (not tooltip offsets) + , elmHeight = thisElement[0].offsetHeight + , elmWidth = thisElement[0].offsetWidth + , elmOffsetLeft = thisElement[0].offsetLeft + , elmOffsetTop = thisElement[0].offsetTop + , elmOffsetRight = winWidth - (elmOffsetLeft + elmWidth) + , elmOffsetBottom = winHeight - (elmHeight + elmOffsetTop) + , offsets = { + 'left': leftOffset, + 'top': topOffset, + 'bottom': bottomOffset, + 'right': rightOffset + } + , posix = { + 'left': elmOffsetLeft, + 'right': elmOffsetRight, + 'top': elmOffsetTop, + 'bottom': elmOffsetBottom + } + , bestPosition = Object.keys(posix).reduce(function (best, key) { + + return posix[best] > posix[key] ? best : key; + }) + , worstOffset = Object.keys(offsets).reduce(function (worst, key) { + + return offsets[worst] < offsets[key] ? worst : key; + }); + + if (originSide !== bestPosition && offsets[worstOffset] < 20) { + + side = bestPosition; + + $scope.tooltipPositioning(side); + $scope.initTooltip(bestPosition); + } + }; + + function onResize() { + $scope.hideTooltip(); + $scope.initTooltip(originSide); + } + + angular.element($window).bind('resize', onResize); + + // destroy the tooltip when the directive is destroyed + // unbind all dom event handlers + $scope.$on('$destroy', function() { + angular.element($window).unbind('resize', onResize); + $scope.clearTriggers(); + theTooltip.remove(); + }); + + if (attr.tooltipTitle) { + attr.$observe('tooltipTitle', function(val) { + $scope.title = val; + $scope.initTooltip(side); + }); + } + + if (attr.title) { + attr.$observe('title', function(val) { + $scope.title = val; + $scope.initTooltip(side); + }); + } + + if (attr.tooltipContent) { + attr.$observe('tooltipContent', function(val) { + $scope.content = val; + $scope.initTooltip(side); + }); + } + + if (attr.tooltipHtml) { + attr.$observe('tooltipHtml', function(val) { + $scope.html = val; + $scope.initTooltip(side); + }); + } + } + }; + }]); +}(angular)); diff --git a/www/lib/angular-tooltips/twitter-bootstrap.html b/www/lib/angular-tooltips/twitter-bootstrap.html new file mode 100644 index 00000000..ac54d69d --- /dev/null +++ b/www/lib/angular-tooltips/twitter-bootstrap.html @@ -0,0 +1,98 @@ +<!doctype html> +<html ng-app="720kb"> +<head> + <link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.1.0/css/font-awesome.min.css"> + <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css"> + <link rel="stylesheet" type="text/css" href="src/css/angular-tooltips.css"> + <title>Angularjs Tooltips</title> +</head> +<body class="center-content" style="margin:50px auto; float:none; max-width:200px;"> + <div class="separator100"></div> + <div class="separator100"></div> + <div class="col6 offset-left2"> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-size="small" tooltip-side="left" title="hey!" > + Small left + </a> + </div> + <div class="separator30"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-size="medium" tooltip-side="left" title="Title attr" > + Medium left + </a> + </div> + <div class="separator30"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-size="large" tooltip-side="left" > + Large left + </a> + </div> + + <div class="separator100"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-size="small" tooltip-side="right" > + Small right + </a> + </div> + + <div class="separator30"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-size="medium" tooltip-side="right" > + Medium right + </a> + </div> + + <div class="separator30"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-size="large" tooltip-side="right" > + Large right + </a> + </div> + <div class="separator100"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-size="small" tooltip-side="top" > + Small top + </a> + </div> + + <div class="separator30"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-size="medium" tooltip-side="top" > + Medium top + </a> + </div> + + <div class="separator30"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-size="large" tooltip-side="top" > + Large top + </a> + </div> + <div class="separator100"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-size="small" tooltip-side="bottom" > + Small bottom + </a> + </div> + + <div class="separator30"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-size="medium" tooltip-side="bottom" > + Medium bottom + </a> + </div> + + <div class="separator30"></div> + <div> + <a class="btn btn-medium bg-info color-white font-bold" tooltips tooltip-content="Yeo man!" tooltip-size="large" tooltip-side="bottom" > + Large bottom + </a> + </div> + <div class="separator100"></div> + </div> + <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.4/angular.min.js"></script> + <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.4/angular-route.min.js"></script> + <script type="text/javascript" src="src/js/angular-tooltips.js"></script> + <script type="text/javascript" src="assets/js/index.js"></script> +</body> +</html> diff --git a/www/lib/angular-tooltips/views/index.html b/www/lib/angular-tooltips/views/index.html new file mode 100644 index 00000000..8efcf770 --- /dev/null +++ b/www/lib/angular-tooltips/views/index.html @@ -0,0 +1,6 @@ +<h4> +Hello view.html +</h4> +<div ng-controller="Ctrl" ng-repeat="x in items"> +{{$index}} +</div> diff --git a/www/lib/angular-touch/.bower.json b/www/lib/angular-touch/.bower.json new file mode 100644 index 00000000..6b3b4751 --- /dev/null +++ b/www/lib/angular-touch/.bower.json @@ -0,0 +1,19 @@ +{ + "name": "angular-touch", + "version": "1.4.3", + "main": "./angular-touch.js", + "ignore": [], + "dependencies": { + "angular": "1.4.3" + }, + "homepage": "https://github.com/angular/bower-angular-touch", + "_release": "1.4.3", + "_resolution": { + "type": "version", + "tag": "v1.4.3", + "commit": "01227e095f879a01d410ed2fff478fd26eb2cc17" + }, + "_source": "git://github.com/angular/bower-angular-touch.git", + "_target": ">=1.2.10", + "_originalSource": "angular-touch" +} diff --git a/www/lib/angular-touch/README.md b/www/lib/angular-touch/README.md new file mode 100644 index 00000000..58933bab --- /dev/null +++ b/www/lib/angular-touch/README.md @@ -0,0 +1,68 @@ +# packaged angular-touch + +This repo is for distribution on `npm` and `bower`. The source for this module is in the +[main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngTouch). +Please file issues and pull requests against that repo. + +## Install + +You can install this package either with `npm` or with `bower`. + +### npm + +```shell +npm install angular-touch +``` + +Then add `ngTouch` as a dependency for your app: + +```javascript +angular.module('myApp', [require('angular-touch')]); +``` + +### bower + +```shell +bower install angular-touch +``` + +Add a `<script>` to your `index.html`: + +```html +<script src="/bower_components/angular-touch/angular-touch.js"></script> +``` + +Then add `ngTouch` as a dependency for your app: + +```javascript +angular.module('myApp', ['ngTouch']); +``` + +## Documentation + +Documentation is available on the +[AngularJS docs site](http://docs.angularjs.org/api/ngTouch). + +## License + +The MIT License + +Copyright (c) 2010-2015 Google, Inc. http://angularjs.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/www/lib/angular-touch/angular-touch.js b/www/lib/angular-touch/angular-touch.js new file mode 100644 index 00000000..fd416a35 --- /dev/null +++ b/www/lib/angular-touch/angular-touch.js @@ -0,0 +1,628 @@ +/** + * @license AngularJS v1.4.3 + * (c) 2010-2015 Google, Inc. http://angularjs.org + * License: MIT + */ +(function(window, angular, undefined) {'use strict'; + +/** + * @ngdoc module + * @name ngTouch + * @description + * + * # ngTouch + * + * The `ngTouch` module provides touch events and other helpers for touch-enabled devices. + * The implementation is based on jQuery Mobile touch event handling + * ([jquerymobile.com](http://jquerymobile.com/)). + * + * + * See {@link ngTouch.$swipe `$swipe`} for usage. + * + * <div doc-module-components="ngTouch"></div> + * + */ + +// define ngTouch module +/* global -ngTouch */ +var ngTouch = angular.module('ngTouch', []); + +function nodeName_(element) { + return angular.lowercase(element.nodeName || (element[0] && element[0].nodeName)); +} + +/* global ngTouch: false */ + + /** + * @ngdoc service + * @name $swipe + * + * @description + * The `$swipe` service is a service that abstracts the messier details of hold-and-drag swipe + * behavior, to make implementing swipe-related directives more convenient. + * + * Requires the {@link ngTouch `ngTouch`} module to be installed. + * + * `$swipe` is used by the `ngSwipeLeft` and `ngSwipeRight` directives in `ngTouch`, and by + * `ngCarousel` in a separate component. + * + * # Usage + * The `$swipe` service is an object with a single method: `bind`. `bind` takes an element + * which is to be watched for swipes, and an object with four handler functions. See the + * documentation for `bind` below. + */ + +ngTouch.factory('$swipe', [function() { + // The total distance in any direction before we make the call on swipe vs. scroll. + var MOVE_BUFFER_RADIUS = 10; + + var POINTER_EVENTS = { + 'mouse': { + start: 'mousedown', + move: 'mousemove', + end: 'mouseup' + }, + 'touch': { + start: 'touchstart', + move: 'touchmove', + end: 'touchend', + cancel: 'touchcancel' + } + }; + + function getCoordinates(event) { + var originalEvent = event.originalEvent || event; + var touches = originalEvent.touches && originalEvent.touches.length ? originalEvent.touches : [originalEvent]; + var e = (originalEvent.changedTouches && originalEvent.changedTouches[0]) || touches[0]; + + return { + x: e.clientX, + y: e.clientY + }; + } + + function getEvents(pointerTypes, eventType) { + var res = []; + angular.forEach(pointerTypes, function(pointerType) { + var eventName = POINTER_EVENTS[pointerType][eventType]; + if (eventName) { + res.push(eventName); + } + }); + return res.join(' '); + } + + return { + /** + * @ngdoc method + * @name $swipe#bind + * + * @description + * The main method of `$swipe`. It takes an element to be watched for swipe motions, and an + * object containing event handlers. + * The pointer types that should be used can be specified via the optional + * third argument, which is an array of strings `'mouse'` and `'touch'`. By default, + * `$swipe` will listen for `mouse` and `touch` events. + * + * The four events are `start`, `move`, `end`, and `cancel`. `start`, `move`, and `end` + * receive as a parameter a coordinates object of the form `{ x: 150, y: 310 }` and the raw + * `event`. `cancel` receives the raw `event` as its single parameter. + * + * `start` is called on either `mousedown` or `touchstart`. After this event, `$swipe` is + * watching for `touchmove` or `mousemove` events. These events are ignored until the total + * distance moved in either dimension exceeds a small threshold. + * + * Once this threshold is exceeded, either the horizontal or vertical delta is greater. + * - If the horizontal distance is greater, this is a swipe and `move` and `end` events follow. + * - If the vertical distance is greater, this is a scroll, and we let the browser take over. + * A `cancel` event is sent. + * + * `move` is called on `mousemove` and `touchmove` after the above logic has determined that + * a swipe is in progress. + * + * `end` is called when a swipe is successfully completed with a `touchend` or `mouseup`. + * + * `cancel` is called either on a `touchcancel` from the browser, or when we begin scrolling + * as described above. + * + */ + bind: function(element, eventHandlers, pointerTypes) { + // Absolute total movement, used to control swipe vs. scroll. + var totalX, totalY; + // Coordinates of the start position. + var startCoords; + // Last event's position. + var lastPos; + // Whether a swipe is active. + var active = false; + + pointerTypes = pointerTypes || ['mouse', 'touch']; + element.on(getEvents(pointerTypes, 'start'), function(event) { + startCoords = getCoordinates(event); + active = true; + totalX = 0; + totalY = 0; + lastPos = startCoords; + eventHandlers['start'] && eventHandlers['start'](startCoords, event); + }); + var events = getEvents(pointerTypes, 'cancel'); + if (events) { + element.on(events, function(event) { + active = false; + eventHandlers['cancel'] && eventHandlers['cancel'](event); + }); + } + + element.on(getEvents(pointerTypes, 'move'), function(event) { + if (!active) return; + + // Android will send a touchcancel if it thinks we're starting to scroll. + // So when the total distance (+ or - or both) exceeds 10px in either direction, + // we either: + // - On totalX > totalY, we send preventDefault() and treat this as a swipe. + // - On totalY > totalX, we let the browser handle it as a scroll. + + if (!startCoords) return; + var coords = getCoordinates(event); + + totalX += Math.abs(coords.x - lastPos.x); + totalY += Math.abs(coords.y - lastPos.y); + + lastPos = coords; + + if (totalX < MOVE_BUFFER_RADIUS && totalY < MOVE_BUFFER_RADIUS) { + return; + } + + // One of totalX or totalY has exceeded the buffer, so decide on swipe vs. scroll. + if (totalY > totalX) { + // Allow native scrolling to take over. + active = false; + eventHandlers['cancel'] && eventHandlers['cancel'](event); + return; + } else { + // Prevent the browser from scrolling. + event.preventDefault(); + eventHandlers['move'] && eventHandlers['move'](coords, event); + } + }); + + element.on(getEvents(pointerTypes, 'end'), function(event) { + if (!active) return; + active = false; + eventHandlers['end'] && eventHandlers['end'](getCoordinates(event), event); + }); + } + }; +}]); + +/* global ngTouch: false, + nodeName_: false +*/ + +/** + * @ngdoc directive + * @name ngClick + * + * @description + * A more powerful replacement for the default ngClick designed to be used on touchscreen + * devices. Most mobile browsers wait about 300ms after a tap-and-release before sending + * the click event. This version handles them immediately, and then prevents the + * following click event from propagating. + * + * Requires the {@link ngTouch `ngTouch`} module to be installed. + * + * This directive can fall back to using an ordinary click event, and so works on desktop + * browsers as well as mobile. + * + * This directive also sets the CSS class `ng-click-active` while the element is being held + * down (by a mouse click or touch) so you can restyle the depressed element if you wish. + * + * @element ANY + * @param {expression} ngClick {@link guide/expression Expression} to evaluate + * upon tap. (Event object is available as `$event`) + * + * @example + <example module="ngClickExample" deps="angular-touch.js"> + <file name="index.html"> + <button ng-click="count = count + 1" ng-init="count=0"> + Increment + </button> + count: {{ count }} + </file> + <file name="script.js"> + angular.module('ngClickExample', ['ngTouch']); + </file> + </example> + */ + +ngTouch.config(['$provide', function($provide) { + $provide.decorator('ngClickDirective', ['$delegate', function($delegate) { + // drop the default ngClick directive + $delegate.shift(); + return $delegate; + }]); +}]); + +ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement', + function($parse, $timeout, $rootElement) { + var TAP_DURATION = 750; // Shorter than 750ms is a tap, longer is a taphold or drag. + var MOVE_TOLERANCE = 12; // 12px seems to work in most mobile browsers. + var PREVENT_DURATION = 2500; // 2.5 seconds maximum from preventGhostClick call to click + var CLICKBUSTER_THRESHOLD = 25; // 25 pixels in any dimension is the limit for busting clicks. + + var ACTIVE_CLASS_NAME = 'ng-click-active'; + var lastPreventedTime; + var touchCoordinates; + var lastLabelClickCoordinates; + + + // TAP EVENTS AND GHOST CLICKS + // + // Why tap events? + // Mobile browsers detect a tap, then wait a moment (usually ~300ms) to see if you're + // double-tapping, and then fire a click event. + // + // This delay sucks and makes mobile apps feel unresponsive. + // So we detect touchstart, touchcancel and touchend ourselves and determine when + // the user has tapped on something. + // + // What happens when the browser then generates a click event? + // The browser, of course, also detects the tap and fires a click after a delay. This results in + // tapping/clicking twice. We do "clickbusting" to prevent it. + // + // How does it work? + // We attach global touchstart and click handlers, that run during the capture (early) phase. + // So the sequence for a tap is: + // - global touchstart: Sets an "allowable region" at the point touched. + // - element's touchstart: Starts a touch + // (- touchcancel ends the touch, no click follows) + // - element's touchend: Determines if the tap is valid (didn't move too far away, didn't hold + // too long) and fires the user's tap handler. The touchend also calls preventGhostClick(). + // - preventGhostClick() removes the allowable region the global touchstart created. + // - The browser generates a click event. + // - The global click handler catches the click, and checks whether it was in an allowable region. + // - If preventGhostClick was called, the region will have been removed, the click is busted. + // - If the region is still there, the click proceeds normally. Therefore clicks on links and + // other elements without ngTap on them work normally. + // + // This is an ugly, terrible hack! + // Yeah, tell me about it. The alternatives are using the slow click events, or making our users + // deal with the ghost clicks, so I consider this the least of evils. Fortunately Angular + // encapsulates this ugly logic away from the user. + // + // Why not just put click handlers on the element? + // We do that too, just to be sure. If the tap event caused the DOM to change, + // it is possible another element is now in that position. To take account for these possibly + // distinct elements, the handlers are global and care only about coordinates. + + // Checks if the coordinates are close enough to be within the region. + function hit(x1, y1, x2, y2) { + return Math.abs(x1 - x2) < CLICKBUSTER_THRESHOLD && Math.abs(y1 - y2) < CLICKBUSTER_THRESHOLD; + } + + // Checks a list of allowable regions against a click location. + // Returns true if the click should be allowed. + // Splices out the allowable region from the list after it has been used. + function checkAllowableRegions(touchCoordinates, x, y) { + for (var i = 0; i < touchCoordinates.length; i += 2) { + if (hit(touchCoordinates[i], touchCoordinates[i + 1], x, y)) { + touchCoordinates.splice(i, i + 2); + return true; // allowable region + } + } + return false; // No allowable region; bust it. + } + + // Global click handler that prevents the click if it's in a bustable zone and preventGhostClick + // was called recently. + function onClick(event) { + if (Date.now() - lastPreventedTime > PREVENT_DURATION) { + return; // Too old. + } + + var touches = event.touches && event.touches.length ? event.touches : [event]; + var x = touches[0].clientX; + var y = touches[0].clientY; + // Work around desktop Webkit quirk where clicking a label will fire two clicks (on the label + // and on the input element). Depending on the exact browser, this second click we don't want + // to bust has either (0,0), negative coordinates, or coordinates equal to triggering label + // click event + if (x < 1 && y < 1) { + return; // offscreen + } + if (lastLabelClickCoordinates && + lastLabelClickCoordinates[0] === x && lastLabelClickCoordinates[1] === y) { + return; // input click triggered by label click + } + // reset label click coordinates on first subsequent click + if (lastLabelClickCoordinates) { + lastLabelClickCoordinates = null; + } + // remember label click coordinates to prevent click busting of trigger click event on input + if (nodeName_(event.target) === 'label') { + lastLabelClickCoordinates = [x, y]; + } + + // Look for an allowable region containing this click. + // If we find one, that means it was created by touchstart and not removed by + // preventGhostClick, so we don't bust it. + if (checkAllowableRegions(touchCoordinates, x, y)) { + return; + } + + // If we didn't find an allowable region, bust the click. + event.stopPropagation(); + event.preventDefault(); + + // Blur focused form elements + event.target && event.target.blur && event.target.blur(); + } + + + // Global touchstart handler that creates an allowable region for a click event. + // This allowable region can be removed by preventGhostClick if we want to bust it. + function onTouchStart(event) { + var touches = event.touches && event.touches.length ? event.touches : [event]; + var x = touches[0].clientX; + var y = touches[0].clientY; + touchCoordinates.push(x, y); + + $timeout(function() { + // Remove the allowable region. + for (var i = 0; i < touchCoordinates.length; i += 2) { + if (touchCoordinates[i] == x && touchCoordinates[i + 1] == y) { + touchCoordinates.splice(i, i + 2); + return; + } + } + }, PREVENT_DURATION, false); + } + + // On the first call, attaches some event handlers. Then whenever it gets called, it creates a + // zone around the touchstart where clicks will get busted. + function preventGhostClick(x, y) { + if (!touchCoordinates) { + $rootElement[0].addEventListener('click', onClick, true); + $rootElement[0].addEventListener('touchstart', onTouchStart, true); + touchCoordinates = []; + } + + lastPreventedTime = Date.now(); + + checkAllowableRegions(touchCoordinates, x, y); + } + + // Actual linking function. + return function(scope, element, attr) { + var clickHandler = $parse(attr.ngClick), + tapping = false, + tapElement, // Used to blur the element after a tap. + startTime, // Used to check if the tap was held too long. + touchStartX, + touchStartY; + + function resetState() { + tapping = false; + element.removeClass(ACTIVE_CLASS_NAME); + } + + element.on('touchstart', function(event) { + tapping = true; + tapElement = event.target ? event.target : event.srcElement; // IE uses srcElement. + // Hack for Safari, which can target text nodes instead of containers. + if (tapElement.nodeType == 3) { + tapElement = tapElement.parentNode; + } + + element.addClass(ACTIVE_CLASS_NAME); + + startTime = Date.now(); + + // Use jQuery originalEvent + var originalEvent = event.originalEvent || event; + var touches = originalEvent.touches && originalEvent.touches.length ? originalEvent.touches : [originalEvent]; + var e = touches[0]; + touchStartX = e.clientX; + touchStartY = e.clientY; + }); + + element.on('touchcancel', function(event) { + resetState(); + }); + + element.on('touchend', function(event) { + var diff = Date.now() - startTime; + + // Use jQuery originalEvent + var originalEvent = event.originalEvent || event; + var touches = (originalEvent.changedTouches && originalEvent.changedTouches.length) ? + originalEvent.changedTouches : + ((originalEvent.touches && originalEvent.touches.length) ? originalEvent.touches : [originalEvent]); + var e = touches[0]; + var x = e.clientX; + var y = e.clientY; + var dist = Math.sqrt(Math.pow(x - touchStartX, 2) + Math.pow(y - touchStartY, 2)); + + if (tapping && diff < TAP_DURATION && dist < MOVE_TOLERANCE) { + // Call preventGhostClick so the clickbuster will catch the corresponding click. + preventGhostClick(x, y); + + // Blur the focused element (the button, probably) before firing the callback. + // This doesn't work perfectly on Android Chrome, but seems to work elsewhere. + // I couldn't get anything to work reliably on Android Chrome. + if (tapElement) { + tapElement.blur(); + } + + if (!angular.isDefined(attr.disabled) || attr.disabled === false) { + element.triggerHandler('click', [event]); + } + } + + resetState(); + }); + + // Hack for iOS Safari's benefit. It goes searching for onclick handlers and is liable to click + // something else nearby. + element.onclick = function(event) { }; + + // Actual click handler. + // There are three different kinds of clicks, only two of which reach this point. + // - On desktop browsers without touch events, their clicks will always come here. + // - On mobile browsers, the simulated "fast" click will call this. + // - But the browser's follow-up slow click will be "busted" before it reaches this handler. + // Therefore it's safe to use this directive on both mobile and desktop. + element.on('click', function(event, touchend) { + scope.$apply(function() { + clickHandler(scope, {$event: (touchend || event)}); + }); + }); + + element.on('mousedown', function(event) { + element.addClass(ACTIVE_CLASS_NAME); + }); + + element.on('mousemove mouseup', function(event) { + element.removeClass(ACTIVE_CLASS_NAME); + }); + + }; +}]); + +/* global ngTouch: false */ + +/** + * @ngdoc directive + * @name ngSwipeLeft + * + * @description + * Specify custom behavior when an element is swiped to the left on a touchscreen device. + * A leftward swipe is a quick, right-to-left slide of the finger. + * Though ngSwipeLeft is designed for touch-based devices, it will work with a mouse click and drag + * too. + * + * To disable the mouse click and drag functionality, add `ng-swipe-disable-mouse` to + * the `ng-swipe-left` or `ng-swipe-right` DOM Element. + * + * Requires the {@link ngTouch `ngTouch`} module to be installed. + * + * @element ANY + * @param {expression} ngSwipeLeft {@link guide/expression Expression} to evaluate + * upon left swipe. (Event object is available as `$event`) + * + * @example + <example module="ngSwipeLeftExample" deps="angular-touch.js"> + <file name="index.html"> + <div ng-show="!showActions" ng-swipe-left="showActions = true"> + Some list content, like an email in the inbox + </div> + <div ng-show="showActions" ng-swipe-right="showActions = false"> + <button ng-click="reply()">Reply</button> + <button ng-click="delete()">Delete</button> + </div> + </file> + <file name="script.js"> + angular.module('ngSwipeLeftExample', ['ngTouch']); + </file> + </example> + */ + +/** + * @ngdoc directive + * @name ngSwipeRight + * + * @description + * Specify custom behavior when an element is swiped to the right on a touchscreen device. + * A rightward swipe is a quick, left-to-right slide of the finger. + * Though ngSwipeRight is designed for touch-based devices, it will work with a mouse click and drag + * too. + * + * Requires the {@link ngTouch `ngTouch`} module to be installed. + * + * @element ANY + * @param {expression} ngSwipeRight {@link guide/expression Expression} to evaluate + * upon right swipe. (Event object is available as `$event`) + * + * @example + <example module="ngSwipeRightExample" deps="angular-touch.js"> + <file name="index.html"> + <div ng-show="!showActions" ng-swipe-left="showActions = true"> + Some list content, like an email in the inbox + </div> + <div ng-show="showActions" ng-swipe-right="showActions = false"> + <button ng-click="reply()">Reply</button> + <button ng-click="delete()">Delete</button> + </div> + </file> + <file name="script.js"> + angular.module('ngSwipeRightExample', ['ngTouch']); + </file> + </example> + */ + +function makeSwipeDirective(directiveName, direction, eventName) { + ngTouch.directive(directiveName, ['$parse', '$swipe', function($parse, $swipe) { + // The maximum vertical delta for a swipe should be less than 75px. + var MAX_VERTICAL_DISTANCE = 75; + // Vertical distance should not be more than a fraction of the horizontal distance. + var MAX_VERTICAL_RATIO = 0.3; + // At least a 30px lateral motion is necessary for a swipe. + var MIN_HORIZONTAL_DISTANCE = 30; + + return function(scope, element, attr) { + var swipeHandler = $parse(attr[directiveName]); + + var startCoords, valid; + + function validSwipe(coords) { + // Check that it's within the coordinates. + // Absolute vertical distance must be within tolerances. + // Horizontal distance, we take the current X - the starting X. + // This is negative for leftward swipes and positive for rightward swipes. + // After multiplying by the direction (-1 for left, +1 for right), legal swipes + // (ie. same direction as the directive wants) will have a positive delta and + // illegal ones a negative delta. + // Therefore this delta must be positive, and larger than the minimum. + if (!startCoords) return false; + var deltaY = Math.abs(coords.y - startCoords.y); + var deltaX = (coords.x - startCoords.x) * direction; + return valid && // Short circuit for already-invalidated swipes. + deltaY < MAX_VERTICAL_DISTANCE && + deltaX > 0 && + deltaX > MIN_HORIZONTAL_DISTANCE && + deltaY / deltaX < MAX_VERTICAL_RATIO; + } + + var pointerTypes = ['touch']; + if (!angular.isDefined(attr['ngSwipeDisableMouse'])) { + pointerTypes.push('mouse'); + } + $swipe.bind(element, { + 'start': function(coords, event) { + startCoords = coords; + valid = true; + }, + 'cancel': function(event) { + valid = false; + }, + 'end': function(coords, event) { + if (validSwipe(coords)) { + scope.$apply(function() { + element.triggerHandler(eventName); + swipeHandler(scope, {$event: event}); + }); + } + } + }, pointerTypes); + }; + }]); +} + +// Left is negative X-coordinate, right is positive. +makeSwipeDirective('ngSwipeLeft', -1, 'swipeleft'); +makeSwipeDirective('ngSwipeRight', 1, 'swiperight'); + + + +})(window, window.angular); diff --git a/www/lib/angular-touch/angular-touch.min.js b/www/lib/angular-touch/angular-touch.min.js new file mode 100644 index 00000000..663f3e65 --- /dev/null +++ b/www/lib/angular-touch/angular-touch.min.js @@ -0,0 +1,13 @@ +/* + AngularJS v1.4.3 + (c) 2010-2015 Google, Inc. http://angularjs.org + License: MIT +*/ +(function(x,s,y){'use strict';function t(f,k,p){n.directive(f,["$parse","$swipe",function(c,e){return function(l,m,g){function h(a){if(!b)return!1;var d=Math.abs(a.y-b.y);a=(a.x-b.x)*k;return r&&75>d&&0<a&&30<a&&.3>d/a}var d=c(g[f]),b,r,a=["touch"];s.isDefined(g.ngSwipeDisableMouse)||a.push("mouse");e.bind(m,{start:function(a,d){b=a;r=!0},cancel:function(a){r=!1},end:function(a,b){h(a)&&l.$apply(function(){m.triggerHandler(p);d(l,{$event:b})})}},a)}}])}var n=s.module("ngTouch",[]);n.factory("$swipe", +[function(){function f(c){c=c.originalEvent||c;var e=c.touches&&c.touches.length?c.touches:[c];c=c.changedTouches&&c.changedTouches[0]||e[0];return{x:c.clientX,y:c.clientY}}function k(c,e){var l=[];s.forEach(c,function(c){(c=p[c][e])&&l.push(c)});return l.join(" ")}var p={mouse:{start:"mousedown",move:"mousemove",end:"mouseup"},touch:{start:"touchstart",move:"touchmove",end:"touchend",cancel:"touchcancel"}};return{bind:function(c,e,l){var m,g,h,d,b=!1;l=l||["mouse","touch"];c.on(k(l,"start"),function(a){h= +f(a);b=!0;g=m=0;d=h;e.start&&e.start(h,a)});var r=k(l,"cancel");if(r)c.on(r,function(a){b=!1;e.cancel&&e.cancel(a)});c.on(k(l,"move"),function(a){if(b&&h){var c=f(a);m+=Math.abs(c.x-d.x);g+=Math.abs(c.y-d.y);d=c;10>m&&10>g||(g>m?(b=!1,e.cancel&&e.cancel(a)):(a.preventDefault(),e.move&&e.move(c,a)))}});c.on(k(l,"end"),function(a){b&&(b=!1,e.end&&e.end(f(a),a))})}}}]);n.config(["$provide",function(f){f.decorator("ngClickDirective",["$delegate",function(k){k.shift();return k}])}]);n.directive("ngClick", +["$parse","$timeout","$rootElement",function(f,k,p){function c(d,b,c){for(var a=0;a<d.length;a+=2){var e=d[a+1],g=c;if(25>Math.abs(d[a]-b)&&25>Math.abs(e-g))return d.splice(a,a+2),!0}return!1}function e(d){if(!(2500<Date.now()-m)){var b=d.touches&&d.touches.length?d.touches:[d],e=b[0].clientX,b=b[0].clientY;if(!(1>e&&1>b||h&&h[0]===e&&h[1]===b)){h&&(h=null);var a=d.target;"label"===s.lowercase(a.nodeName||a[0]&&a[0].nodeName)&&(h=[e,b]);c(g,e,b)||(d.stopPropagation(),d.preventDefault(),d.target&& +d.target.blur&&d.target.blur())}}}function l(d){d=d.touches&&d.touches.length?d.touches:[d];var b=d[0].clientX,c=d[0].clientY;g.push(b,c);k(function(){for(var a=0;a<g.length;a+=2)if(g[a]==b&&g[a+1]==c){g.splice(a,a+2);break}},2500,!1)}var m,g,h;return function(d,b,h){var a=f(h.ngClick),k=!1,q,n,t,v;b.on("touchstart",function(a){k=!0;q=a.target?a.target:a.srcElement;3==q.nodeType&&(q=q.parentNode);b.addClass("ng-click-active");n=Date.now();a=a.originalEvent||a;a=(a.touches&&a.touches.length?a.touches: +[a])[0];t=a.clientX;v=a.clientY});b.on("touchcancel",function(a){k=!1;b.removeClass("ng-click-active")});b.on("touchend",function(a){var d=Date.now()-n,f=a.originalEvent||a,u=(f.changedTouches&&f.changedTouches.length?f.changedTouches:f.touches&&f.touches.length?f.touches:[f])[0],f=u.clientX,u=u.clientY,w=Math.sqrt(Math.pow(f-t,2)+Math.pow(u-v,2));k&&750>d&&12>w&&(g||(p[0].addEventListener("click",e,!0),p[0].addEventListener("touchstart",l,!0),g=[]),m=Date.now(),c(g,f,u),q&&q.blur(),s.isDefined(h.disabled)&& +!1!==h.disabled||b.triggerHandler("click",[a]));k=!1;b.removeClass("ng-click-active")});b.onclick=function(a){};b.on("click",function(b,c){d.$apply(function(){a(d,{$event:c||b})})});b.on("mousedown",function(a){b.addClass("ng-click-active")});b.on("mousemove mouseup",function(a){b.removeClass("ng-click-active")})}}]);t("ngSwipeLeft",-1,"swipeleft");t("ngSwipeRight",1,"swiperight")})(window,window.angular); +//# sourceMappingURL=angular-touch.min.js.map diff --git a/www/lib/angular-touch/angular-touch.min.js.map b/www/lib/angular-touch/angular-touch.min.js.map new file mode 100644 index 00000000..029bd32c --- /dev/null +++ b/www/lib/angular-touch/angular-touch.min.js.map @@ -0,0 +1,8 @@ +{ +"version":3, +"file":"angular-touch.min.js", +"lineCount":12, +"mappings":"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", +"sources":["angular-touch.js"], +"names":["window","angular","undefined","makeSwipeDirective","directiveName","direction","eventName","ngTouch","directive","$parse","$swipe","scope","element","attr","validSwipe","coords","startCoords","deltaY","Math","abs","y","deltaX","x","valid","MAX_VERTICAL_DISTANCE","MIN_HORIZONTAL_DISTANCE","MAX_VERTICAL_RATIO","swipeHandler","pointerTypes","isDefined","push","bind","start","event","cancel","end","$apply","triggerHandler","$event","module","factory","getCoordinates","originalEvent","touches","length","e","changedTouches","clientX","clientY","getEvents","eventType","res","forEach","pointerType","POINTER_EVENTS","join","move","eventHandlers","totalX","totalY","lastPos","active","on","events","MOVE_BUFFER_RADIUS","preventDefault","config","$provide","decorator","$delegate","shift","$timeout","$rootElement","checkAllowableRegions","touchCoordinates","i","CLICKBUSTER_THRESHOLD","x1","y1","y2","splice","onClick","PREVENT_DURATION","Date","now","lastPreventedTime","lastLabelClickCoordinates","target","lowercase","nodeName","stopPropagation","blur","onTouchStart","clickHandler","ngClick","tapping","tapElement","startTime","touchStartX","touchStartY","srcElement","nodeType","parentNode","addClass","ACTIVE_CLASS_NAME","removeClass","diff","dist","sqrt","pow","TAP_DURATION","MOVE_TOLERANCE","addEventListener","disabled","onclick","element.onclick","touchend"] +} diff --git a/www/lib/angular-touch/bower.json b/www/lib/angular-touch/bower.json new file mode 100644 index 00000000..5a0e75ed --- /dev/null +++ b/www/lib/angular-touch/bower.json @@ -0,0 +1,9 @@ +{ + "name": "angular-touch", + "version": "1.4.3", + "main": "./angular-touch.js", + "ignore": [], + "dependencies": { + "angular": "1.4.3" + } +} diff --git a/www/lib/angular-touch/index.js b/www/lib/angular-touch/index.js new file mode 100644 index 00000000..0478a404 --- /dev/null +++ b/www/lib/angular-touch/index.js @@ -0,0 +1,2 @@ +require('./angular-touch'); +module.exports = 'ngTouch'; diff --git a/www/lib/angular-touch/package.json b/www/lib/angular-touch/package.json new file mode 100644 index 00000000..f7ca1dcc --- /dev/null +++ b/www/lib/angular-touch/package.json @@ -0,0 +1,26 @@ +{ + "name": "angular-touch", + "version": "1.4.3", + "description": "AngularJS module for touch events and helpers for touch-enabled devices", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://github.com/angular/angular.js.git" + }, + "keywords": [ + "angular", + "framework", + "browser", + "touch", + "client-side" + ], + "author": "Angular Core Team <angular-core+npm@google.com>", + "license": "MIT", + "bugs": { + "url": "https://github.com/angular/angular.js/issues" + }, + "homepage": "http://angularjs.org" +} |
