diff --git a/.gitignore b/.gitignore index 207ff55..3fc0a6e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,4 @@ -.DS_Store node_modules/ -localConfig.json -build/bellows.zip -build/bellows-style.css -build/bellows.css -build/bellows.js +bower_components/ +.DS_Store +/.sass-cache/ diff --git a/Gruntfile.js b/Gruntfile.js index cceb06a..4145392 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,110 +1,55 @@ +'use strict' + +var path = require('path'); + module.exports = function(grunt) { + var _ = grunt.util._; + + // By default, we load all local tasks from the tasks directory. + grunt.file.expand('tasks/*').forEach(function(task) { + grunt.loadTasks(task); + }); + + // Populate the config object + var config = {}; + grunt.file.expand('tasks/config/*').forEach(function(configPath) { + // Get the grunt-task name to put in the config which is based on the + // name of the config file + var configName = configPath.match(/\/([^\/]*)\.js/)[1]; + var option = require(path.join(__dirname + '/' + configPath))(grunt); + config[configName] = _.extend(config[configName] || {}, option); + }); - // Project configuration. - grunt.initConfig({ + grunt.initConfig(_.extend({ pkg: grunt.file.readJSON('package.json'), - localConfig: (function(){ - try { - return grunt.file.readJSON('localConfig.json') - } catch(e) { - return {}; - } - })(), releaseName: '<%= pkg.name %>-<%= pkg.version %>', - releaseMessage: '<%= pkg.name %> release <%= pkg.version %>', - clean: { - buildProducts: "build/" - }, - connect: { - server: { - options: { - hostname: '0.0.0.0', - port: 3000, - base: '.' - } - } - }, - watch: { - files: ["src/**/*"], - tasks: ['build'] - }, - copy: { - main: { - files: [ - {expand: true, flatten: true, src: ['src/**'], dest: 'build/', filter: 'isFile'} - ] - } - }, - uglify: { - options: { - banner: '/*! <%= pkg.name %> <%= pkg.version %> <%= grunt.template.today("yyyy-mm-dd") %> */\n' - }, - build: { - src: 'src/bellows.js', - dest: 'build/bellows.min.js' - } - }, - cssmin: { - core: { - src: 'src/bellows.css', - dest: 'build/bellows.min.css' - }, - style: { - src: 'src/bellows-style.css', - dest: 'build/bellows-style.min.css' - } - }, - zip: { - "build/bellows.zip": ["src/bellows.js", "src/bellows.css", - "src/bellows-style.css"] - }, - s3: { - key: '<%= localConfig.aws.key %>', - secret: '<%= localConfig.aws.secret %>', - bucket: '<%= localConfig.aws.bucket %>', - access: "public-read", - headers: { "Cache-Control": "max-age=1200" }, - upload: [ - { // build - src: "build/*", - dest: "modules/bellows/<%= pkg.version %>/", - rel: "build" - } - ] - }, - release: { - options: { - folder: '.', - npm: false, - bump: false, - add: false, - commit: false, - file: 'bower.json', - github: { - repo: 'mobify/bellows', - usernameVar: 'GITHUB_USERNAME', - passwordVar: 'GITHUB_TOKEN' - } - } - } - }); + releaseMessage: '<%= pkg.name %> release <%= pkg.version %>' + }, config)); - // Load the task plugins - grunt.loadNpmTasks('grunt-contrib-uglify'); - grunt.loadNpmTasks('grunt-contrib-watch'); - grunt.loadNpmTasks('grunt-contrib-connect'); - grunt.loadNpmTasks('grunt-css'); - grunt.loadNpmTasks('grunt-shell'); - grunt.loadNpmTasks('grunt-zip'); - grunt.loadNpmTasks('grunt-s3'); - grunt.loadNpmTasks('grunt-clean'); - grunt.loadNpmTasks('grunt-contrib-copy'); - grunt.loadNpmTasks('grunt-release'); + // load npm tasks + var npmTasks = [ + 'grunt-contrib-uglify', + 'grunt-contrib-watch', + 'grunt-contrib-connect', + 'grunt-css', + 'grunt-shell', + 'grunt-contrib-clean', + 'grunt-contrib-copy', + 'grunt-autoprefixer', + 'grunt-contrib-sass', + 'grunt-mocha-phantomjs' + ]; - // Default task(s). - grunt.registerTask('serve', ['connect', 'watch']); - grunt.registerTask('build', ['copy', 'uglify', 'cssmin', 'zip']); - grunt.registerTask('publish', ['build', 'release', 's3']) - grunt.registerTask('default', 'build'); + npmTasks.forEach(function(taskName) { + if (!grunt.task._tasks[taskName]) { + grunt.loadNpmTasks(taskName); + } + }); + grunt.registerTask('serve', ['build-dist', 'connect:server', 'watch']); + grunt.registerTask('build-dist', ['copy', 'uglify', 'sass', 'autoprefixer', 'cssmin']); + grunt.registerTask('release', ['test', 'shell:tagRelease']); + grunt.registerTask('test', ['build-dist', 'connect:test', 'mocha_phantomjs']); + grunt.registerTask('test:browser', ['build-dist', 'connect:test:keepalive']); + grunt.registerTask('default', 'build-dist'); }; diff --git a/LICENSE b/LICENSE index 0f37219..648b279 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013 Mobify Research & Development Inc. +Copyright (c) 2014 Mobify Research & Development Inc. 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 diff --git a/README.md b/README.md index ae89efa..a21ca1b 100644 --- a/README.md +++ b/README.md @@ -1,200 +1,298 @@ # Mobify Bellows -A responsive, mobile-first accordion UI module for progressive disclosure on the web. +A mobile-first accordion UI module for progressive disclosure on the web. -## Docs +## Documentation -You can find docs and examples here: http://mobify.github.io/bellows. +You can find full documentation and examples here: http://mobify.github.io/bellows. -Checkout the `gh-pages` branch to make changes to the docs. +## Requirements + +* [Zepto](http://zeptojs.com/) +* [Velocity.js](http://velocityjs.org) + +### Velocity Shim + +If you are using Zepto you will need to include the velocity shim **before** velocity. If Velocity.js ever stops requiring jQuery, we will be able to stop using this shim. + +### jQuery Support + +Bellows supports jQuery but is not actively developed for it. You should be able to use Bellows directly with jQuery 2.0 and simply drop the Velocity.js shim in those cases. While we don't actively support jQuery for Bellows, we welcome any and all issues and PRs to help us make it work. + + +## Installation + +Bellows can be installed using bower: + +``` +bower install bellows +``` ## Usage - - - - - - +
+

Content

+
+ +
+
+

Header

+
+
+

Content

+
+
+ - - - - - - + + + -## Methods + + + + + +``` + +## Initializing the plugin ### bellows() Initializes the bellows. - $('.m-bellows').bellows(); +```js +$('.bellows').bellows(); +``` ### bellows(options) Initialize with options. - $('.m-bellows').bellows({ - { - ... - options (refer below) - ... - } - }); +```js +$('.bellows').bellows({ + singleItemOpen: false, + duration: 200, + easing: 'swing', + open: function(e, ui) {}, + opened: function(e, ui) {}, + close: function(e, ui) {}, + closed: function(e, ui) {} +}); +``` -### Storing bellows object for future use +#### Options - var $bellows = $(".m-bellows"); // A Zepto element array is returned - var bellows = $bellows[0].bellows; // We access the appropriate bellows from the above array +##### singleItemOpen -### unbind() +default: `false` -Removes any tap, mouse, and other event handlers from the bellows. +When set to `true` will force only one item open at a time. - bellows.unbind(); +```js +$('.bellows').bellows({ + singleItemOpen: true +}); +``` -### bind() +##### duration -Restores the tap, mouse, and other event handlers for the bellows. +default: `200` - bellows.bind(); +Sets the duration for the animation. -### destroy() +```js +$('.bellows').bellows({ + duration: 600 +}); +``` -Unbinds the events from the bellows, and removes it from the DOM. +##### easing - bellows.destroy(); // destroys the DOM element and the jQuery bindings - bellows = null; // destroys the Mobify bellows object as well +default: `swing` -### open($item) +Sets the easing for the animation. Bellows takes all of the same easing properties that [Velocity.js](http://julian.com/research/velocity) accepts. -Open the selected bellows item +> * [jQuery UI's easings](http://easings.net/) and CSS3's easings ("ease", "ease-in", "ease-out", and "ease-in-out"), which are pre-packaged into Velocity. A bonus "spring" easing (sampled in the CSS Support pane) is also included. +* CSS3's bezier curves: Pass in a four-item array of bezier points. (Refer to [Cubic-Bezier.com](http://cubic-bezier.com/) for crafing custom bezier curves.) +* Spring physics: Pass a two-item array in the form of [ tension, friction ]. A higher tension (default: 600) increases total speed and bounciness. A lower friction (default: 20) increases ending vibration speed. +* Step easing: Pass a one-item array in the form of [ steps ]. The animation will jump toward its end values using the specified number of steps. - bellows.open($(".m-item").eq(2)); +For more information, check out [Velocity's docs on easing](http://julian.com/research/velocity/#easing). -### close($item) - -Close the selected bellows item +```js +$('.bellows').bellows({ + easing: 'ease-in-out' +}); +``` + +##### open + +default: `function(e, ui) {}` + +Triggered every time the selected bellows item is starting to open. - bellows.close($("#some-item")); +**Parameters** -### recalculateItemHeight($item) +| Parameter name | Description | +|----------------|-------------| +| **e** | An Event object passed to the callback | +| **ui** | An object containing any associated data for use inside the callback | -Recalculate the heights of bellows item elements. This is used when the heights of the content have changed after creation of the bellows. +```js +$('.bellows').bellows({ + open: function(e, ui) { + // ui.item contains the item opening + } +}); +``` - bellows.recalculateItemHeight($(".m-item")); +##### opened -## Class names +default: `function(e, ui) {}` -Set the class names for the different elements, if deviating from the defaults. - - $(".m-bellows").bellows({ - closedClass: 'm-closed', - openedClass: 'm-opened', - activeClass: 'm-active', - contentClass: 'm-content', - innerContentClass: 'm-inner-content', - headerClass: 'm-header', - itemClass: 'm-item' - }); +Triggered every time the selected bellows item has finished opening. -## Event hooks +**Parameters** -### onTransitionDone: functionName +| Parameter name | Description | +|----------------|-------------| +| **e** | An Event object passed to the callback | +| **ui** | An object containing any associated data for use inside the callback | -Execute this function every time the selected bellows item is opened or closed. +```js +$('.bellows').bellows({ + opened: function(e, ui) { + // ui.item contains the item that opened + } +}); +``` - $(".m-bellows").bellows({ - onTransitionDone: function() { console.log("Animation done"); } - }); +##### close -### onOpened: functionName +default: `function(e, ui) {}` -Execute this function every time the selected bellows item is opened. +Triggered every time an bellows item is starting to close. - $(".m-bellows").bellows({ - onOpened: function() { console.log("Opened"); } - }); +| Parameter name | Description | +|----------------|-------------| +| **e** | An Event object passed to the callback | +| **ui** | An object containing any associated data for use inside the callback | -### onClosed: functionName +```js +$('.bellows').bellows({ + close: function(e, ui) { + // ui.item contains the item closing + } +}); +``` -Execute this function every time an bellows item is closed +##### closed + +default: `function(e, ui) {}` + +Triggered every time an bellows item is finished closing. + +| Parameter name | Description | +|----------------|-------------| +| **e** | An Event object passed to the callback | +| **ui** | An object containing any associated data for use inside the callback | + +```js +$('.bellows').bellows({ + closed: function(e, ui) { + // ui.item contains the item that closed + } +}); +``` + +### Storing bellows object for future use + +```js +var $bellows = $('.bellows'); +``` + +## Methods + +### Open + +Open the selected bellows item by element reference + +```js +$bellows.bellows('open', $('.bellows__item')); +``` + +or by index + +```js +$bellows.bellows('open', 1); +``` + +### Close - $(".m-bellows").bellows({ - onClosed: function() { console.log("Closed"); } - }); +Close the selected bellows item by element reference -## Browser Compatibility +```js +$bellows.bellows('close', $('.bellows__item')); +``` + +or by index +```js +$bellows.bellows('close', 1); +``` + +## Browser Compatibility | Browser | Version | Support | |-------------------|---------|----------------------------| -| Safari | 4.0+ | Supported. | -| Firefox | 3.5-3.6 | Degraded. No transitions. | -| Firefox | 4.0+ | Supported | -| Chrome | 9.0+ | Supported | -| Opera | 12.0+ | Supported. | -| Internet Explorer | 6-7.0 | Not Supported | -| Internet Explorer | 8.0 | Degraded. No transitions. | -| Internet Explorer | 9.0 | Degraded. No transitions. | -| Internet Explorer | 10.0 | Supported | -| Mobile Safari | 3.1.x | Degraded. No transitions | -| Mobile Safari | 4.0+ | Supported | -| Android Browser | 2.1+ | Supported | -| Chrome (Android) | 1.0+ | Supported | -| Firefox (Android) | 1.0+ | Supported | -| Windows Phone | 7.5 | Degraded. No transitions. | - -## Building +| Mobile Safari | 4.0.x | Degraded. No transitions. | +| Mobile Safari | 5.0+ | Supported. | +| Android Browser | 4.0+ | Supported. | +| Android Browser | 2.3.x | Degraded. No transitions. | +| Chrome (Android) | 1.0+ | Supported. | + + +## Building a distribution + ### Requirements -* [node.js 0.8.x/npm](http://nodejs.org/download/) +* [node.js 0.10.x/npm](http://nodejs.org/download/) +* [Grunt](http://gruntjs.com/) + * Install with `npm install -g grunt-cli` +* [Bower](http://bower.io/) + * Install with `npm install -g bower` ### Steps -1. `npm install -g grunt-cli` -2. `npm install` -3. `grunt` +1. `npm install` +1. `bower install` +1. `grunt build-dist` + +The `dist` directory will be populated with minified versions of the css and javascript files for distribution and use with whatever build system you might use. The `src` directory has our raw unminified Sass and Javascript files if you prefer to work with those. -The build directory will be populated with minified versions of the css and -javascript files and a .zip of the original source files (for distribution and -use with whatever build system you might use). +## License +_MIT License. Bellows is Copyright © 2014 Mobify. It is free software and may be redistributed under the terms specified in the LICENSE file._ diff --git a/bower.json b/bower.json index 0be375b..fc679e5 100644 --- a/bower.json +++ b/bower.json @@ -1,24 +1,26 @@ { "name": "bellows", - "version": "0.3.4", + "version": "2.0.0", "homepage": "https://github.com/mobify/bellows", "authors": [ "Mobify " ], - "description": "A responsive, mobile first accordion UI module", - "main": "build/bellows.min.js", + "description": "A mobile first accordion UI plugin", + "main": "dist/bellows.min.js", "keywords": [ "bellows", "accordion", "toggle", - "responsive", "mobile", "mobify" ], "license": "MIT", "ignore": [ - "**/.*", - "node_modules", - "bower_components" - ] + "./!(src|dist|lib|templates)" + ], + "dependencies": { + "velocity": "0.11.7", + "requirejs-text": "~2.0.12", + "requirejs": "~2.1.14" + } } diff --git a/build/bellows-style.min.css b/build/bellows-style.min.css deleted file mode 100644 index ce23be4..0000000 --- a/build/bellows-style.min.css +++ /dev/null @@ -1 +0,0 @@ -.m-bello{color:#000}.m-bellows .m-header{height:54px;line-height:52px;padding:0 20px;border:solid 1px #ededed;border-width:1px 0;position:relative;cursor:pointer;z-index:5;background:#fff;background:-moz-linear-gradient(top,#fff 0,#f6f6f6 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,#fff),color-stop(100%,#f6f6f6));background:-webkit-linear-gradient(top,#fff 0,#f6f6f6 100%);background:-o-linear-gradient(top,#fff 0,#f6f6f6 100%);background:-ms-linear-gradient(top,#fff 0,#f6f6f6 100%);background:linear-gradient(to bottom,#fff 0,#f6f6f6 100%)}.m-bellows .m-header:hover,.m-bellows .hm-eader:focus{z-index:2;background:#fff;background:-moz-linear-gradient(top,#fff 0,#f6f6f6 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,#fff),color-stop(100%,#f6f6f6));background:-webkit-linear-gradient(top,#f6f6f6 0,#e6e6e6 100%);background:-o-linear-gradient(top,#fff 0,#f6f6f6 100%);background:-ms-linear-gradient(top,#fff 0,#f6f6f6 100%);background:linear-gradient(to bottom,#f6f6f6 0,#f0f0f0 100%);border-bottom-color:#d6d6d6}.m-bellows .m-item:first-child .m-header{-webkit-border-top-left-radius:9px;-webkit-border-top-right-radius:9px;-moz-border-radius-topleft:9px;-moz-border-radius-topright:9px;border-top-left-radius:9px;border-top-right-radius:9px}.m-bellows .m-item:last-child .m-header,.m-bellows .m-active:last-child .m-inner-content{-webkit-border-bottom-left-radius:9px;-webkit-border-bottom-right-radius:9px;-moz-border-radius-bottomleft:9px;-moz-border-radius-bottomright:9px;border-bottom-left-radius:9px;border-bottom-right-radius:9px}.m-bellows .m-active:last-child .m-header{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.m-bellows .m-header a{color:#000}.m-bellows .m-inner-content{padding:10px 20px;background:#fff}.m-bellows h2{margin:0;color:#000;text-shadow:none}.m-indicators-images .m-header:after{content:"";display:block;position:absolute;top:50%;right:10px;margin-top:-11px;z-index:4;width:22px;height:22px;background:url(../img/bellows.png) 0 -20px no-repeat}.m-indicators-images .m-active .m-header:after{background-position:0 -43px}@media only screen and (-moz-min-device-pixel-ratio:1.5),only screen and (-o-min-device-pixel-ratio:3/2),only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min-devicepixel-ratio:1.5),only screen and (min-resolution:1.5dppx){.m-indicators-images .m-header:after{background-position:-11px 0;background-size:33px 43px}.m-indicators-images .m-active .m-header:after{background-position:-11px -22px}}.m-arrows .m-active .m-header:after{top:100%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none;border-top-color:#f6f6f6;border-width:20px;left:50%;margin-left:-20px;z-index:9}.m-arrows .m-active .m-header:hover:after{border-top-color:#e6e6e6}.m-indicators-css .m-header:after{content:"+";color:#999;text-shadow:#fff 0 1px 0;display:block;position:absolute;top:50%;right:15px;margin-top:-13px;z-index:9;font-size:26px;line-height:22px;font-weight:700}.m-indicators-css .m-active .m-header:after{content:"\2212"}.m-css-advanced .m-header:after{color:rgba(255,255,255,.9);font-weight:400;text-align:center;text-shadow:none;background:#c5c5c5;background:-moz-linear-gradient(top,#c5c5c5 0,#acacac 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,#c5c5c5),color-stop(100%,#acacac));background:-webkit-linear-gradient(top,#c5c5c5 0,#acacac 100%);background:-o-linear-gradient(top,#c5c5c5 0,#acacac 100%);background:-ms-linear-gradient(top,#c5c5c5 0,#acacac 100%);background:linear-gradient(to bottom,#c5c5c5 0,#acacac 100%);font-size:22px;line-height:16px;width:20px;height:20px;margin-top:-10px;-webkit-border-radius:25px;-moz-border-radius:25px;border-radius:25px;-webkit-box-shadow:#fff 0 1px 0;-moz-box-shadow:#fff 0 1px 0;box-shadow:#fff 0 1px 0}.m-css-advanced .m-header:hover:after{color:#fff;background:#b4b4b4;background:-moz-linear-gradient(top,#b4b4b4 0,#a3a3a3 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,#b4b4b4),color-stop(100%,#a3a3a3));background:-webkit-linear-gradient(top,#b4b4b4 0,#a3a3a3 100%);background:-o-linear-gradient(top,#b4b4b4 0,#a3a3a3 100%);background:-ms-linear-gradient(top,#b4b4b4 0,#a3a3a3 100%);background:linear-gradient(to bottom,#b4b4b4 0,#a3a3a3 100%)}.m-css-advanced .m-active .m-header:after{background:#8a8a8a;background:-moz-linear-gradient(top,#8a8a8a 0,#a3a3a3 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,#8a8a8a),color-stop(100%,#a3a3a3));background:-webkit-linear-gradient(top,#8a8a8a 0,#a3a3a3 100%);background:-o-linear-gradient(top,#8a8a8a 0,#a3a3a3 100%);background:-ms-linear-gradient(top,#8a8a8a 0,#a3a3a3 100%);background:linear-gradient(to bottom,#8a8a8a 0,#a3a3a3 100%);-webkit-box-shadow:#fff 0 -1px 0;-moz-box-shadow:#fff 0 -1px 0;box-shadow:#fff 0 -1px 0} \ No newline at end of file diff --git a/build/bellows.min.css b/build/bellows.min.css deleted file mode 100644 index 9bb422f..0000000 --- a/build/bellows.min.css +++ /dev/null @@ -1 +0,0 @@ -.m-bellows{list-style:none}.m-bellows li{list-style:none;margin:0}.m-bellows .m-content{-webkit-transition:max-height .5s ease;-moz-transition:max-height .5s ease;-o-transition:max-height .5s ease;transition:max-height .5s ease;overflow:hidden;max-height:0}.m-bellows .m-active>.m-content{max-height:10000px}.m-bellows .m-header{z-index:1;position:relative;margin-top:-1px;pointer-events:cursor}.m-bellows h3{margin:0}.m-bellows .m-header>a{pointer-events:none} \ No newline at end of file diff --git a/build/bellows.min.js b/build/bellows.min.js deleted file mode 100644 index 6424ca8..0000000 --- a/build/bellows.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! bellows 0.3.4 2014-04-10 */ -var Mobify=window.Mobify=window.Mobify||{};Mobify.$=Mobify.$||window.Zepto||window.jQuery,Mobify.UI=Mobify.UI||{},function(n,e){n.support=n.support||{},n.extend(n.support,{touch:"ontouchend"in e})}(Mobify.$,document),Mobify.UI.Utils=function(n){function e(){if(/iPhone\ OS\ 3_1/.test(navigator.userAgent))return void 0;var n,e=document.createElement("fakeelement"),t={transition:"transitionEnd transitionend",OTransition:"oTransitionEnd",MSTransition:"msTransitionEnd",MozTransition:"transitionend",WebkitTransition:"webkitTransitionEnd"};for(n in t)if(void 0!==e.style[n])return t[n]}var t={},o=n.support;return t.events=o.touch?{down:"touchstart",move:"touchmove",up:"touchend"}:{down:"mousedown",move:"mousemove",up:"mouseup"},t.getCursorPosition=o.touch?function(n){return n=n.originalEvent||n,{x:n.touches[0].clientX,y:n.touches[0].clientY}}:function(n){return{x:n.clientX,y:n.clientY}},t.getProperty=function(n){for(var e=["Webkit","Moz","O","ms",""],t=document.createElement("div").style,o=0;e.length>o;++o)if(void 0!==t[e[o]+n])return e[o]+n},n.extend(t.events,{transitionend:e()}),t}(Mobify.$),Mobify.UI.Bellows=function(n,e){n.support;var t=function(e,t){return this.settings=n.extend({dragRadius:10,closedClass:"m-closed",openedClass:"m-opened",activeClass:"m-active",contentClass:"m-content",innerContentClass:"m-inner-content",headerClass:"m-header",itemClass:"m-item",moduleClass:"m-bellows",onTransitionDone:null,onOpened:null,onClosed:null},t||{}),this.$element=n(e),n.extend(this,this.bind()),this};return t.prototype.bind=function(){function t(){var e=n(this).parent();e.hasClass(g)&&n(this).parent().removeClass(C),s(e.hasClass(g)?"closing":"opening"),o(p)}function o(e){var t=0;e.children("."+M).each(function(){var e=n(this);t+=e.height()}),e.css("min-height",t+"px")}function i(n){var t=n.children("."+b),s=t.children(),r="outerHeight"in s?s.outerHeight():s.height();t.css("max-height",1.5*r+"px"),e.events.transitionend&&p.css("min-height",p.height()+"px"),setTimeout(function(){$currentBellows=n.closest("."+x),o($currentBellows),$parentBellows=$currentBellows.parent().closest("."+x),$parentBellows.length>0&&i(n.parent().closest("."+M))},150)}function s(n){"opening"===n&&"function"==typeof m.onOpened&&m.onOpened.apply(this,arguments),"closing"===n&&"function"==typeof m.onClosed&&m.onClosed.apply(this,arguments),"function"==typeof m.onTransitionDone&&m.onTransitionDone.apply(this,arguments)}function r(n){n.hasClass(g)&&s("closing"),n.removeClass(y),n.addClass(g),e.events.transitionend||n.removeClass(C),n.children("."+b).css("max-height","0px")}function a(n){n.hasClass(y)&&s("opening"),n.addClass(C),n.removeClass(g),n.addClass(y),i(n)}function l(n){h=e.getCursorPosition(n)}function u(n){f=e.getCursorPosition(n)}function c(){if(!(f&&(dx=h.x-f.x,dy=h.y-f.y,f=void 0,dx*dx+dy*dy>v*v))){var e=n(this).parent();e.hasClass(C)?r(e):a(e)}}function d(n){n.preventDefault()}var h,f,p=this.$element,m=this.settings,v=m.dragRadius,y=m.openedClass,g=m.closedClass,C=m.activeClass,b=m.contentClass,w=m.headerClass,M=m.itemClass,x=m.moduleClass,$=location.hash,T=p.find("."+w+' a[href="'+$+'"]');T.length?a(T.parent()):p.find("."+y).length&&a(p.find("."+y));var U=p.children("."+M).children("."+w);return U.on(e.events.down,l).on(e.events.move,u).on(e.events.up,c).on("click",d),e.events.transitionend&&p.on(e.events.transitionend,"."+b,t),{settings:m,open:a,close:r,recalculateItemHeight:i}},t.prototype.unbind=function(){this.$element.off()},t.prototype.destroy=function(){this.unbind(),this.$element.remove()},t}(Mobify.$,Mobify.UI.Utils),function(n){n.fn.bellows=function(e){return this.each(function(){var t=(n(this),this.bellows);t||(t=new Mobify.UI.Bellows(this,e)),this.bellows=t})}}(Mobify.$); \ No newline at end of file diff --git a/circle.yml b/circle.yml new file mode 100644 index 0000000..abc2cfc --- /dev/null +++ b/circle.yml @@ -0,0 +1,10 @@ +machine: + node: + version: 0.10.21 + +test: + pre: + - npm install + - bower install + override: + - grunt test diff --git a/dist/bellows-theme.css b/dist/bellows-theme.css new file mode 100644 index 0000000..35a6a93 --- /dev/null +++ b/dist/bellows-theme.css @@ -0,0 +1,59 @@ +.bellows { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; } + +.bellows__header { + position: relative; + padding: 15px 20px; + border: 1px solid #2980b9; + border-width: 0 0 1px; + background: #3498db; + color: white; + -webkit-tap-highlight-color: transparent; } + .bellows__header:active { + background: #2980b9; } + .bellows__header::before, .bellows__header::after { + content: ''; + position: absolute; + top: 50%; + right: 20px; + z-index: 2; + display: block; + width: 16px; + height: 4px; + margin-top: -2px; + background: white; + pointer-events: none; + -webkit-transition: -webkit-transform 0.25s ease-in-out; + transition: transform 0.25s ease-in-out; } + .bellows__header::before { + content: ''; + -webkit-transform: rotate(0deg); + -ms-transform: rotate(0deg); + transform: rotate(0deg); } + .bellows__header::after { + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); } + .bellows__item.bellows--is-open > .bellows__header::before, .bellows__item.bellows--is-opening > .bellows__header::before { + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); } + .bellows__item.bellows--is-open > .bellows__header::after, .bellows__item.bellows--is-opening > .bellows__header::after { + -webkit-transform: rotate(360deg); + -ms-transform: rotate(360deg); + transform: rotate(360deg); } + .bellows__item:last-child > .bellows__header { + border-bottom: 0; } + .bellows__header h1, + .bellows__header h2, + .bellows__header h3, + .bellows__header h4 { + margin: 0; } + +.bellows__content { + padding: 20px; + border: 1px solid #ecf0f1; } + .bellows__content .bellows { + margin-top: 20px; } diff --git a/dist/bellows-theme.min.css b/dist/bellows-theme.min.css new file mode 100644 index 0000000..1a68f4b --- /dev/null +++ b/dist/bellows-theme.min.css @@ -0,0 +1 @@ +.bellows{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bellows__header{position:relative;padding:15px 20px;border:1px solid #2980b9;border-width:0 0 1px;background:#3498db;color:#fff;-webkit-tap-highlight-color:transparent}.bellows__header:active{background:#2980b9}.bellows__header::before,.bellows__header::after{content:'';position:absolute;top:50%;right:20px;z-index:2;display:block;width:16px;height:4px;margin-top:-2px;background:#fff;pointer-events:none;-webkit-transition:-webkit-transform .25s ease-in-out;transition:transform .25s ease-in-out}.bellows__header::before{content:'';-webkit-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg)}.bellows__header::after{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.bellows__item.bellows--is-open>.bellows__header::before,.bellows__item.bellows--is-opening>.bellows__header::before{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.bellows__item.bellows--is-open>.bellows__header::after,.bellows__item.bellows--is-opening>.bellows__header::after{-webkit-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)}.bellows__item:last-child>.bellows__header{border-bottom:0}.bellows__header h1,.bellows__header h2,.bellows__header h3,.bellows__header h4{margin:0}.bellows__content{padding:20px;border:1px solid #ecf0f1}.bellows__content .bellows{margin-top:20px} \ No newline at end of file diff --git a/dist/bellows.css b/dist/bellows.css new file mode 100644 index 0000000..3120fa2 --- /dev/null +++ b/dist/bellows.css @@ -0,0 +1,9 @@ +.bellows__item:not(.bellows--is-open) > .bellows__content { + display: none; } + +.bellows__item.bellows--is-open > .bellows__content-wrapper, +.bellows__item.bellows--is-closing > .bellows__content-wrapper { + display: block; } + +.bellows__content-wrapper { + display: none; } diff --git a/dist/bellows.js b/dist/bellows.js new file mode 100644 index 0000000..7fe7057 --- /dev/null +++ b/dist/bellows.js @@ -0,0 +1,236 @@ +/* + Bellows.js v2.0.0 + */ +(function (factory) { + if (typeof define === 'function' && define.amd) { + /* + In AMD environments, you will need to define an alias + to your selector engine. i.e. either zepto or jQuery. + */ + define([ + '$', + 'velocity' + ], factory); + } else { + /* + Browser globals + */ + var framework = window.Zepto || window.jQuery; + factory(framework, framework.Velocity); + } +}(function($, Velocity) { + var PLUGIN_NAME = 'bellows'; + var noop = function() {}; + + var ITEM_CLASS = 'bellows__item'; + var OPENED_CLASS = 'bellows--is-open'; + var OPENING_CLASS = 'bellows--is-opening'; + var CLOSING_CLASS = 'bellows--is-closing'; + + var selectors = { + ITEM_HEADER: '> .bellows__item > .bellows__header', + ITEM_CONTENT_WRAPPER: '> .bellows__content-wrapper', + ITEM_CONTENT: '> .bellows__item > .bellows__content' + }; + + function Bellows(element, options) { + this._init(element, options); + } + + Bellows.VERSION = '2.0.0'; + + Bellows.DEFAULTS = { + singleItemOpen: false, + event: 'click', + duration: 200, + easing: 'swing', + open: noop, + opened: noop, + close: noop, + closed: noop + }; + + Bellows.prototype._init = function(element, options) { + this.options = $.extend(true, {}, Bellows.DEFAULTS, options); + + this.$bellows = $(element); + + this.$bellows + .find(selectors.ITEM_CONTENT) + // wrap content section of each item to facilitate padding + .wrap('
') + // add aria-hidden attribute to all hidden content wrappers + .parents('.bellows__item:not(.bellows--is-open)') + .find('.bellows__content-wrapper') + .attr('aria-hidden', true); + + this._bindEvents(); + }; + + Bellows.prototype._bindEvents = function() { + var plugin = this; + + // We use tappy here to eliminate the 300ms delay on clicking elements + this.$bellows + .find(selectors.ITEM_HEADER) + .bind(this.options.event, function(e) { + e.preventDefault(); + + plugin.toggle($(this).parent()); + }); + }; + + /* + Gets an element's height using Velocity's built-in property cache. + Used for getting heights before animations, for animating into an + element's space. + */ + Bellows.prototype._getHeight = function($element) { + return parseFloat(Velocity.CSS.getPropertyValue($element[0], 'height')); + }; + + /* + Sets the height of bellows so we animate into + the space rather than re-flowing the entire document + */ + Bellows.prototype._setHeight = function(height) { + this.$bellows.css('height', height || ''); + }; + + /* + Allow items to be found using an index + */ + Bellows.prototype._item = function(item) { + if (typeof item === 'number') { + item = this.$bellows.find('.' + ITEM_CLASS).eq(item); + } + + return item; + }; + + Bellows.prototype._trigger = function(eventName, data) { + eventName in this.options && this.options[eventName].call(this, $.Event(PLUGIN_NAME + ':' + eventName, { bubbles: false }), data); + }; + + Bellows.prototype.toggle = function($item) { + $item = this._item($item); + + this[$item.hasClass(OPENED_CLASS) ? 'close' : 'open']($item); + }; + + Bellows.prototype.open = function($item) { + $item = this._item($item); + + if ($item.hasClass(OPENED_CLASS)) { + return; + } + + var plugin = this; + var $contentWrapper = $item.find(selectors.ITEM_CONTENT_WRAPPER); + + if (this.options.singleItemOpen) { + this.closeAll(); + } + + this._trigger('open', { item: $item }); + + Velocity + .animate($contentWrapper, 'slideDown', { + begin: function() { + plugin._setHeight(plugin._getHeight(plugin.$bellows) + plugin._getHeight($contentWrapper)); + $item.addClass(OPENING_CLASS); + }, + duration: this.options.duration, + easing: this.options.easing, + complete: function() { + $item + .removeClass(OPENING_CLASS) + .addClass(OPENED_CLASS) + .attr('aria-hidden', true); + + plugin._setHeight(); + + plugin._trigger('opened', { item: $item }); + } + }); + }; + + Bellows.prototype.close = function($item) { + $item = this._item($item); + + if (!$item.hasClass(OPENED_CLASS)) { + return; + } + + var plugin = this; + var $contentWrapper = $item.find(selectors.ITEM_CONTENT_WRAPPER); + + this._trigger('close', { item: $item }); + + Velocity + .animate($contentWrapper, 'slideUp', { + begin: function() { + plugin._setHeight(plugin._getHeight(plugin.$bellows)); + $item + .removeClass(OPENED_CLASS) + .addClass(CLOSING_CLASS); + }, + duration: this.options.duration, + easing: this.options.easing, + complete: function() { + $item + .removeClass(CLOSING_CLASS) + .removeAttr('aria-hidden'); + + plugin._setHeight(); + + plugin._trigger('closed', { item: $item }); + } + }); + }; + + Bellows.prototype.closeAll = function() { + var plugin = this; + + this.$bellows.find('.' + OPENED_CLASS).each(function() { + plugin.close($(this)); + }); + }; + + /* + Bellows plugin definition + */ + $.fn.bellows = function(option) { + var args = Array.prototype.slice.call(arguments); + + return this.each(function() { + var $this = $(this); + var bellows = $this.data(PLUGIN_NAME); + var isMethodCall = typeof option === 'string'; + + // If bellows isn't initialized, we lazy-load initialize it. If it's + // already initialized, we can safely ignore the call. + if (!bellows) { + if (isMethodCall) { + throw 'cannot call methods on bellows prior to initialization; attempted to call method "' + option + '"'; + } + $this.data(PLUGIN_NAME, (bellows = new Bellows(this, option))); + } + + // invoke a public method on bellows, and skip private methods + if (isMethodCall) { + if (option.charAt(0) === '_' || typeof bellows[option] !== 'function') { + throw 'no such method "' + option + '" for bellows'; + } + + bellows[option].apply(bellows, args.length > 1 ? args.slice(1) : null); + } + }); + }; + + $.fn.bellows.Constructor = Bellows; + + $('[data-bellows]').bellows(); + + return $; +})); diff --git a/dist/bellows.min.css b/dist/bellows.min.css new file mode 100644 index 0000000..c42509a --- /dev/null +++ b/dist/bellows.min.css @@ -0,0 +1 @@ +.bellows__item:not(.bellows--is-open)>.bellows__content{display:none}.bellows__item.bellows--is-open>.bellows__content-wrapper,.bellows__item.bellows--is-closing>.bellows__content-wrapper{display:block}.bellows__content-wrapper{display:none} \ No newline at end of file diff --git a/dist/bellows.min.js b/dist/bellows.min.js new file mode 100644 index 0000000..1846938 --- /dev/null +++ b/dist/bellows.min.js @@ -0,0 +1,2 @@ +/*! bellows 2.0.0 2014-08-26 */ +(function(t){if("function"==typeof define&&define.amd)define(["$","velocity"],t);else{var e=window.Zepto||window.jQuery;t(e,e.Velocity)}})(function(t,e){function i(t,e){this._init(t,e)}var o="bellows",n=function(){},s="bellows__item",l="bellows--is-open",r="bellows--is-opening",a="bellows--is-closing",p={ITEM_HEADER:"> .bellows__item > .bellows__header",ITEM_CONTENT_WRAPPER:"> .bellows__content-wrapper",ITEM_CONTENT:"> .bellows__item > .bellows__content"};return i.VERSION="2.0.0",i.DEFAULTS={singleItemOpen:!1,event:"click",duration:200,easing:"swing",open:n,opened:n,close:n,closed:n},i.prototype._init=function(e,o){this.options=t.extend(!0,{},i.DEFAULTS,o),this.$bellows=t(e),this.$bellows.find(p.ITEM_CONTENT).wrap('
').parents(".bellows__item:not(.bellows--is-open)").find(".bellows__content-wrapper").attr("aria-hidden",!0),this._bindEvents()},i.prototype._bindEvents=function(){var e=this;this.$bellows.find(p.ITEM_HEADER).bind(this.options.event,function(i){i.preventDefault(),e.toggle(t(this).parent())})},i.prototype._getHeight=function(t){return parseFloat(e.CSS.getPropertyValue(t[0],"height"))},i.prototype._setHeight=function(t){this.$bellows.css("height",t||"")},i.prototype._item=function(t){return"number"==typeof t&&(t=this.$bellows.find("."+s).eq(t)),t},i.prototype._trigger=function(e,i){e in this.options&&this.options[e].call(this,t.Event(o+":"+e,{bubbles:!1}),i)},i.prototype.toggle=function(t){t=this._item(t),this[t.hasClass(l)?"close":"open"](t)},i.prototype.open=function(t){if(t=this._item(t),!t.hasClass(l)){var i=this,o=t.find(p.ITEM_CONTENT_WRAPPER);this.options.singleItemOpen&&this.closeAll(),this._trigger("open",{item:t}),e.animate(o,"slideDown",{begin:function(){i._setHeight(i._getHeight(i.$bellows)+i._getHeight(o)),t.addClass(r)},duration:this.options.duration,easing:this.options.easing,complete:function(){t.removeClass(r).addClass(l).attr("aria-hidden",!0),i._setHeight(),i._trigger("opened",{item:t})}})}},i.prototype.close=function(t){if(t=this._item(t),t.hasClass(l)){var i=this,o=t.find(p.ITEM_CONTENT_WRAPPER);this._trigger("close",{item:t}),e.animate(o,"slideUp",{begin:function(){i._setHeight(i._getHeight(i.$bellows)),t.removeClass(l).addClass(a)},duration:this.options.duration,easing:this.options.easing,complete:function(){t.removeClass(a).removeAttr("aria-hidden"),i._setHeight(),i._trigger("closed",{item:t})}})}},i.prototype.closeAll=function(){var e=this;this.$bellows.find("."+l).each(function(){e.close(t(this))})},t.fn.bellows=function(e){var n=Array.prototype.slice.call(arguments);return this.each(function(){var s=t(this),l=s.data(o),r="string"==typeof e;if(!l){if(r)throw'cannot call methods on bellows prior to initialization; attempted to call method "'+e+'"';s.data(o,l=new i(this,e))}if(r){if("_"===e.charAt(0)||"function"!=typeof l[e])throw'no such method "'+e+'" for bellows';l[e].apply(l,n.length>1?n.slice(1):null)}})},t.fn.bellows.Constructor=i,t("[data-bellows]").bellows(),t}); \ No newline at end of file diff --git a/examples/animation.html b/examples/animation.html new file mode 100644 index 0000000..f908f3e --- /dev/null +++ b/examples/animation.html @@ -0,0 +1,59 @@ + + + + Examples of Bellows in action + + + + + + + + + + +

Bellows Examples

+ +

Different Animation Timings

+ +

You can pass Bellows alternate animation duration and easing when you initialize it. The different easings available are described in the readme

+ +
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+ + + + + diff --git a/examples/assets/css/examples.css b/examples/assets/css/examples.css new file mode 100644 index 0000000..d342043 --- /dev/null +++ b/examples/assets/css/examples.css @@ -0,0 +1,16 @@ +* { + box-sizing: border-box; +} + +html { + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + -webkit-text-size-adjust: 100%; +} + +.bellows p { + margin: 0; +} + +p + p { + margin-bottom: 10px; +} \ No newline at end of file diff --git a/examples/assets/js/config.js b/examples/assets/js/config.js new file mode 100644 index 0000000..5d4524a --- /dev/null +++ b/examples/assets/js/config.js @@ -0,0 +1,16 @@ +require.config({ + baseUrl: '../', + almond: true, + paths: { + 'text': 'bower_components/requirejs-text/text', + '$': 'lib/zeptojs/dist/zepto', + 'velocity': 'bower_components/velocity/velocity', + 'bellows': 'dist/bellows', + 'setup-bellows': 'examples/assets/js/setup-bellows' + }, + 'shim': { + '$': { + exports: '$' + } + } +}); diff --git a/examples/assets/js/main.js b/examples/assets/js/main.js new file mode 100644 index 0000000..5e3e058 --- /dev/null +++ b/examples/assets/js/main.js @@ -0,0 +1,9 @@ +require(['config'], function() { + require([ + '$', + 'setup-bellows' + ], + function($, setupBellows) { + setupBellows.init(); + }); +}); diff --git a/examples/assets/js/setup-bellows.js b/examples/assets/js/setup-bellows.js new file mode 100644 index 0000000..4835e3d --- /dev/null +++ b/examples/assets/js/setup-bellows.js @@ -0,0 +1,36 @@ +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['bellows'], factory); + } else { + // Browser globals + root.setupBellows = factory(window.Zepto || window.jQuery); + } +}(this, function ($) { + + // Default bellows initialization (/examples/default.html) + function init() { + $('.bellows.default').bellows(); + + // Bellows with custom animation duration and easing (/examples/animation.html) + $('.bellows.animation').bellows({ + duration: 1000, + easing: "easeInOutCubic" + }); + + // Bellows within bellows within bellows... (/examples/inception.html) + $('.bellows.inception, .bellows.inception .bellows').bellows(); + + // Bellows with single item open (/examples/single.html) + $('.bellows.single').bellows({ + singleItemOpen: true + }); + + // Enable active states + $(document).on('touchstart', function() {}); + } + + return { + init: init + }; +})); \ No newline at end of file diff --git a/examples/dataattr.html b/examples/dataattr.html new file mode 100644 index 0000000..9ae139a --- /dev/null +++ b/examples/dataattr.html @@ -0,0 +1,59 @@ + + + + Examples of Bellows in action + + + + + + + + + + +

Bellows Examples

+ +

Auto-initialization with Data Attribute

+ +

This example shows how you can initialize a bellows with default settings without needing to call it in your JS.

+ +
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+ + + + + diff --git a/examples/default.html b/examples/default.html new file mode 100644 index 0000000..332482e --- /dev/null +++ b/examples/default.html @@ -0,0 +1,61 @@ + + + + Examples of Bellows in action + + + + + + + + + + +

Bellows Examples

+ +

Default Bellows

+ +
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+ +

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+ +

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+ + + + + diff --git a/examples/inception.html b/examples/inception.html new file mode 100644 index 0000000..bc54fe4 --- /dev/null +++ b/examples/inception.html @@ -0,0 +1,117 @@ + + + + Examples of Bellows in action + + + + + + + + + + +

Bellows Examples

+ +

Bellows within Bellows

+ +

Bellows, unlike the rules of Inception, will allow you to go as many levels deep as you'd like. You can insert Bellows within Bellows to your heart's content.

+ +
+
+
+

Header

+
+
+
+
+
+

Header

+
+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+ + + + + diff --git a/examples/index.html b/examples/index.html new file mode 100644 index 0000000..eb276a1 --- /dev/null +++ b/examples/index.html @@ -0,0 +1,27 @@ + + + + Examples of Bellows in action + + + + + + + +

Bellows Examples

+ + + + + + + + diff --git a/examples/norequire.html b/examples/norequire.html new file mode 100644 index 0000000..c3dfc12 --- /dev/null +++ b/examples/norequire.html @@ -0,0 +1,65 @@ + + + + Examples of Bellows in action + + + + + + + + + + +

Bellows Examples

+ +

Default Bellows

+ +
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+ +

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+ +

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+ + + + + + + + + diff --git a/examples/single.html b/examples/single.html new file mode 100644 index 0000000..d5716b0 --- /dev/null +++ b/examples/single.html @@ -0,0 +1,59 @@ + + + + Examples of Bellows in action + + + + + + + + + + +

Bellows Examples

+ +

Single Item Open

+ +

The singleItemOpen option enables you to set Bellows to only have one of its items open at any given time.

+ +
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+ + + + + diff --git a/index.html b/index.html deleted file mode 100644 index 823850b..0000000 --- a/index.html +++ /dev/null @@ -1,107 +0,0 @@ - - - - Examples of Bellows in action - - - - - - -
    -
  • - -
    -
    -
      -
    • - -
      -
      - Item 1 content -
      -
      -
    • -
    • - -
      -
      - Item 2 content
      - Item 2 content -
      -
      -
    • -
    • - -
      -
      - Item 3 content
      - Item 3 content
      - Item 3 content -
      -
      -
    • -
    -
    -
    -
  • -
  • - -
    -
    -
      -
    • - -
      -
      - Item 1 content -
      -
      -
    • -
    • - -
      -
      - Item 2 content
      - Item 2 content -
      -
      -
    • -
    • - -
      -
      - Item 3 content
      - Item 3 content
      - Item 3 content -
      -
      -
    • -
    -
    -
    -
  • -
- - - - - - - diff --git a/lib/zeptojs/.bower.json b/lib/zeptojs/.bower.json new file mode 100644 index 0000000..d2c508d --- /dev/null +++ b/lib/zeptojs/.bower.json @@ -0,0 +1,14 @@ +{ + "name": "zeptojs", + "homepage": "https://github.com/madrobby/zepto", + "version": "1.1.3", + "_release": "1.1.3", + "_resolution": { + "type": "version", + "tag": "v1.1.3", + "commit": "fef90efe1c98f43f6ce1e1f2f793077e1c6994c5" + }, + "_source": "git://github.com/madrobby/zepto.git", + "_target": "1.1.3", + "_originalSource": "zeptojs" +} \ No newline at end of file diff --git a/lib/zeptojs/.gitignore b/lib/zeptojs/.gitignore new file mode 100644 index 0000000..c5963fd --- /dev/null +++ b/lib/zeptojs/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +pkg +*.swp +docs/* +.jhw-cache +.rbenv-version +public +node_modules +temp/ +npm-debug.log \ No newline at end of file diff --git a/lib/zeptojs/.travis.yml b/lib/zeptojs/.travis.yml new file mode 100644 index 0000000..b2f29ef --- /dev/null +++ b/lib/zeptojs/.travis.yml @@ -0,0 +1,9 @@ +script: script/test +language: node_js +notifications: + campfire: + template: '%{message} [%{branch}] %{repository}/%{commit} %{author} %{build_url}' + rooms: + secure: VR6rWk0YhezBWnD8jPjSD8h/Q83S3NT0F34Au1vswt+/+Ku19S8X44vGVUG+NYdYyhg7uOqUaPN1Jr3KCpdcXgHEpUYiyBGJ8ebltavcjeHYWqK6ghcqgSnbDkifuC7Eu/9LcrOMOXgt+zkXjiVXW3+zyGVDcrs4cQ2vGY2DTYA= +node_js: +- 0.10 diff --git a/lib/zeptojs/CONTRIBUTING.md b/lib/zeptojs/CONTRIBUTING.md new file mode 100644 index 0000000..a554d7a --- /dev/null +++ b/lib/zeptojs/CONTRIBUTING.md @@ -0,0 +1,45 @@ +## Contributing to Zepto + +**Thanks for helping out!** + +In order for your code to make it in, several conditions must be met: + +* It's more likely your pull request will make it in if you adhere to **Zepto's + project goals**. Be sure to read the README in its entirety before setting out + to code. +* Please talk to the maintainers (@madrobby and @mislav) first if you want + to write a plugin, those are better kept in their own repositories. +* Fix only ONE thing or have only ONE feature in your pull request. + If you have multiple unrelated code updates, please submit a separate pull request for each one. +* **Your pull request must be written in English and be accompanied by a + detailed description**, ideally something we can use as documentation. + If you're not fluent in English, try your best and let us know so we'll help! +* Changes to jQuery-based API methods **must match their jQuery counterparts**. +* Please **do not just copy code from jQuery**. Zepto strives for API compatibility, + but has different goals for code style and size and target platforms. + In case you do copy code, you must clearly indicate the origin of the code, and + which license applies to it. However, it is likely your patch will be denied. +* **All code must have tests, and all tests must pass.** See the README on running the test suite. +* Please **also test manually** on as many target platforms you have access to, + but at least on latest Chrome (desktop) and Firefox (desktop). + See http://zeptojs.com for a full list of platforms. +* It's required that you follow Zepto's **code style guidelines** (see below) + +Whew, now that we have that out of the way thanks again! + +## Code style guidelines + +* Two spaces "soft tabs" indentation +* Remove any trailing whitespace from the end of lines +* `function name() { }` for named functions +* `function(){ }` for anonymous functions +* No curly braces for single-line control flow statements such as `if` & friends +* Don't write [semicolons that are optional][optional] +* Put a single semicolon _before_ statements that start with `(` or `[` + (see above article as for why it's needed) +* Use long, descriptive variable and method names +* Use blank lines to separate "paragraphs" of code for readability +* Use comments to describe non-obvious code behavior + + + [optional]: http://mislav.uniqpath.com/2010/05/semicolons/ diff --git a/lib/zeptojs/MIT-LICENSE b/lib/zeptojs/MIT-LICENSE new file mode 100644 index 0000000..5a62a30 --- /dev/null +++ b/lib/zeptojs/MIT-LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2010-2014 Thomas Fuchs +http://zeptojs.com/ + +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/lib/zeptojs/README.md b/lib/zeptojs/README.md new file mode 100644 index 0000000..b00bceb --- /dev/null +++ b/lib/zeptojs/README.md @@ -0,0 +1,225 @@ +# Zepto.js – a minimalist JavaScript library + +Zepto is a minimalist JavaScript library for modern browsers with a +largely jQuery-compatible API. If you use jQuery, you already know how to use Zepto. + +See [zeptojs.com][] for an extended introduction, downloads +and documentation. + +Zepto.js is licensed under the terms of the MIT License. + +Want to give us money or a tip? Don't. +Instead please donate to [charity: water](http://charitywater.org/). + +## Building + +[![Build Status](https://secure.travis-ci.org/madrobby/zepto.png?branch=master)](http://travis-ci.org/madrobby/zepto) + +The official site offers a download of the default distribution of Zepto. This +is good for starting out. However, at some point you might want to add some +optional modules and remove some of the default ones you don't need, to keep the +size at a minimum. That's when you need to check out Zepto's source code and use +the build commands. + +You will need Node.js installed on your system. + +~~~ sh +$ npm install +$ npm run-script dist + +# do a custom build +$ MODULES="zepto event data" npm run-script dist +~~~ + +The resulting files are: + +1. `dist/zepto.js` +2. `dist/zepto.min.js` + +If you install CoffeeScript globally, you can run `make` directly: + +~~~ sh +$ coffee make dist +$ MODULES="zepto event data ..." ./make dist +~~~ + +## Zepto modules + +Zepto modules are individual files in the "src/" directory. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
module default description
zeptoCore module; contains most methods
eventEvent handling via on() & off()
ajaxXMLHttpRequest and JSONP functionality
formSerialize & submit web forms
ieSupport for Internet Explorer 10+ on the desktop and Windows Phone 8
detectProvides $.os and $.browser information
fxThe animate() method
fx_methods + Animated show, hide, toggle, + and fade*() methods. +
assets + Experimental support for cleaning up iOS memory after removing + image elements from the DOM. +
data + A full-blown data() method, capable of storing arbitrary + objects in memory. +
deferred + Provides $.Deferred promises API. + Depends on the "callbacks" module. +
callbacks + Provides $.Callbacks for use in "deferred" module. +
selector + Experimental jQuery + CSS extensions support for functionality such as $('div:first') and + el.is(':visible'). +
touch + Fires tap– and swipe–related events on touch devices. This works with both + `touch` (iOS, Android) and `pointer` events (Windows Phone). +
gestureFires pinch gesture events on touch devices
stackProvides andSelf & end() chaining methods
ios3 + String.prototype.trim and Array.prototype.reduce methods + (if they are missing) for compatibility with iOS 3.x. +
+ +## Contributing + +Please read our [contribution guidelines](https://github.com/madrobby/zepto/blob/master/CONTRIBUTING.md) +for information on how to contribute. + +Get in touch: + +* @[zeptojs](http://twitter.com/zeptojs) + +### Write documentation + +Zepto docs are written in Markdown and live in the ["gh-pages" branch][docs]. +They are published on [zeptojs.com][]. + +You can use GitHub's web interface to make quick changes to documentation for +specific Zepto features +([example: ajaxSettings](https://github.com/madrobby/zepto/blob/gh-pages/ajax/_posts/1900-01-01-Z-ajaxSettings.md)). +This will submit a pull request to us that we can review. + +### Report a bug + +1. Check if the bug is already fixed in the [master branch][master] since the + last release. +2. Check [existing issues][issues]. Open a new one, including exact browser & + platform information. For better formatting of your report, see + [GitHub-flavored Markdown][mkd]. + +### Running tests + +You will need to install [PhantomJS][]. On OS X, that's easy: + +~~~ sh +$ brew install phantomjs +~~~ + +To run the automated tests: + +~~~ sh +$ npm test +~~~ + +To run a test server, which you can hit with your browsers and devices: + +~~~ sh +$ npm start +~~~ + +Go to `http://your-ip-address:3000/` on your browser and follow the +instructions. For your convenience test failures and exceptions will be +reported to the the console you started the test server in (as well as +the browser console if available). + + [zeptojs.com]: http://zeptojs.com + [master]: https://github.com/madrobby/zepto/commits/master + [issues]: https://github.com/madrobby/zepto/issues + [docs]: https://github.com/madrobby/zepto/tree/gh-pages#readme + [mkd]: http://github.github.com/github-flavored-markdown/ + [evidence.js]: https://github.com/tobie/Evidence + [phantomjs]: http://code.google.com/p/phantomjs/wiki/Installation diff --git a/lib/zeptojs/dist/zepto.js b/lib/zeptojs/dist/zepto.js new file mode 100644 index 0000000..25c6bbe --- /dev/null +++ b/lib/zeptojs/dist/zepto.js @@ -0,0 +1,1811 @@ +/* Zepto 1.1.3 - zepto event ajax form fx selector stack data - zeptojs.com/license */ + + +var Zepto = (function() { + var undefined, key, $, classList, emptyArray = [], slice = emptyArray.slice, filter = emptyArray.filter, + document = window.document, + elementDisplay = {}, classCache = {}, + cssNumber = { 'column-count': 1, 'columns': 1, 'font-weight': 1, 'line-height': 1,'opacity': 1, 'z-index': 1, 'zoom': 1 }, + fragmentRE = /^\s*<(\w+|!)[^>]*>/, + singleTagRE = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, + tagExpanderRE = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, + rootNodeRE = /^(?:body|html)$/i, + capitalRE = /([A-Z])/g, + + // special attributes that should be get/set via method calls + methodAttributes = ['val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset'], + + adjacencyOperators = [ 'after', 'prepend', 'before', 'append' ], + table = document.createElement('table'), + tableRow = document.createElement('tr'), + containers = { + 'tr': document.createElement('tbody'), + 'tbody': table, 'thead': table, 'tfoot': table, + 'td': tableRow, 'th': tableRow, + '*': document.createElement('div') + }, + readyRE = /complete|loaded|interactive/, + simpleSelectorRE = /^[\w-]*$/, + class2type = {}, + toString = class2type.toString, + zepto = {}, + camelize, uniq, + tempParent = document.createElement('div'), + propMap = { + 'tabindex': 'tabIndex', + 'readonly': 'readOnly', + 'for': 'htmlFor', + 'class': 'className', + 'maxlength': 'maxLength', + 'cellspacing': 'cellSpacing', + 'cellpadding': 'cellPadding', + 'rowspan': 'rowSpan', + 'colspan': 'colSpan', + 'usemap': 'useMap', + 'frameborder': 'frameBorder', + 'contenteditable': 'contentEditable' + }, + isArray = Array.isArray || + function(object){ return object instanceof Array } + + zepto.matches = function(element, selector) { + if (!selector || !element || element.nodeType !== 1) return false + var matchesSelector = element.webkitMatchesSelector || element.mozMatchesSelector || + element.oMatchesSelector || element.matchesSelector + if (matchesSelector) return matchesSelector.call(element, selector) + // fall back to performing a selector: + var match, parent = element.parentNode, temp = !parent + if (temp) (parent = tempParent).appendChild(element) + match = ~zepto.qsa(parent, selector).indexOf(element) + temp && tempParent.removeChild(element) + return match + } + + function type(obj) { + return obj == null ? String(obj) : + class2type[toString.call(obj)] || "object" + } + + function isFunction(value) { return type(value) == "function" } + function isWindow(obj) { return obj != null && obj == obj.window } + function isDocument(obj) { return obj != null && obj.nodeType == obj.DOCUMENT_NODE } + function isObject(obj) { return type(obj) == "object" } + function isPlainObject(obj) { + return isObject(obj) && !isWindow(obj) && Object.getPrototypeOf(obj) == Object.prototype + } + function likeArray(obj) { return typeof obj.length == 'number' } + + function compact(array) { return filter.call(array, function(item){ return item != null }) } + function flatten(array) { return array.length > 0 ? $.fn.concat.apply([], array) : array } + camelize = function(str){ return str.replace(/-+(.)?/g, function(match, chr){ return chr ? chr.toUpperCase() : '' }) } + function dasherize(str) { + return str.replace(/::/g, '/') + .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') + .replace(/([a-z\d])([A-Z])/g, '$1_$2') + .replace(/_/g, '-') + .toLowerCase() + } + uniq = function(array){ return filter.call(array, function(item, idx){ return array.indexOf(item) == idx }) } + + function classRE(name) { + return name in classCache ? + classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)')) + } + + function maybeAddPx(name, value) { + return (typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value + } + + function defaultDisplay(nodeName) { + var element, display + if (!elementDisplay[nodeName]) { + element = document.createElement(nodeName) + document.body.appendChild(element) + display = getComputedStyle(element, '').getPropertyValue("display") + element.parentNode.removeChild(element) + display == "none" && (display = "block") + elementDisplay[nodeName] = display + } + return elementDisplay[nodeName] + } + + function children(element) { + return 'children' in element ? + slice.call(element.children) : + $.map(element.childNodes, function(node){ if (node.nodeType == 1) return node }) + } + + // `$.zepto.fragment` takes a html string and an optional tag name + // to generate DOM nodes nodes from the given html string. + // The generated DOM nodes are returned as an array. + // This function can be overriden in plugins for example to make + // it compatible with browsers that don't support the DOM fully. + zepto.fragment = function(html, name, properties) { + var dom, nodes, container + + // A special case optimization for a single tag + if (singleTagRE.test(html)) dom = $(document.createElement(RegExp.$1)) + + if (!dom) { + if (html.replace) html = html.replace(tagExpanderRE, "<$1>") + if (name === undefined) name = fragmentRE.test(html) && RegExp.$1 + if (!(name in containers)) name = '*' + + container = containers[name] + container.innerHTML = '' + html + dom = $.each(slice.call(container.childNodes), function(){ + container.removeChild(this) + }) + } + + if (isPlainObject(properties)) { + nodes = $(dom) + $.each(properties, function(key, value) { + if (methodAttributes.indexOf(key) > -1) nodes[key](value) + else nodes.attr(key, value) + }) + } + + return dom + } + + // `$.zepto.Z` swaps out the prototype of the given `dom` array + // of nodes with `$.fn` and thus supplying all the Zepto functions + // to the array. Note that `__proto__` is not supported on Internet + // Explorer. This method can be overriden in plugins. + zepto.Z = function(dom, selector) { + dom = dom || [] + dom.__proto__ = $.fn + dom.selector = selector || '' + return dom + } + + // `$.zepto.isZ` should return `true` if the given object is a Zepto + // collection. This method can be overriden in plugins. + zepto.isZ = function(object) { + return object instanceof zepto.Z + } + + // `$.zepto.init` is Zepto's counterpart to jQuery's `$.fn.init` and + // takes a CSS selector and an optional context (and handles various + // special cases). + // This method can be overriden in plugins. + zepto.init = function(selector, context) { + var dom + // If nothing given, return an empty Zepto collection + if (!selector) return zepto.Z() + // Optimize for string selectors + else if (typeof selector == 'string') { + selector = selector.trim() + // If it's a html fragment, create nodes from it + // Note: In both Chrome 21 and Firefox 15, DOM error 12 + // is thrown if the fragment doesn't begin with < + if (selector[0] == '<' && fragmentRE.test(selector)) + dom = zepto.fragment(selector, RegExp.$1, context), selector = null + // If there's a context, create a collection on that context first, and select + // nodes from there + else if (context !== undefined) return $(context).find(selector) + // If it's a CSS selector, use it to select nodes. + else dom = zepto.qsa(document, selector) + } + // If a function is given, call it when the DOM is ready + else if (isFunction(selector)) return $(document).ready(selector) + // If a Zepto collection is given, just return it + else if (zepto.isZ(selector)) return selector + else { + // normalize array if an array of nodes is given + if (isArray(selector)) dom = compact(selector) + // Wrap DOM nodes. + else if (isObject(selector)) + dom = [selector], selector = null + // If it's a html fragment, create nodes from it + else if (fragmentRE.test(selector)) + dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null + // If there's a context, create a collection on that context first, and select + // nodes from there + else if (context !== undefined) return $(context).find(selector) + // And last but no least, if it's a CSS selector, use it to select nodes. + else dom = zepto.qsa(document, selector) + } + // create a new Zepto collection from the nodes found + return zepto.Z(dom, selector) + } + + // `$` will be the base `Zepto` object. When calling this + // function just call `$.zepto.init, which makes the implementation + // details of selecting nodes and creating Zepto collections + // patchable in plugins. + $ = function(selector, context){ + return zepto.init(selector, context) + } + + function extend(target, source, deep) { + for (key in source) + if (deep && (isPlainObject(source[key]) || isArray(source[key]))) { + if (isPlainObject(source[key]) && !isPlainObject(target[key])) + target[key] = {} + if (isArray(source[key]) && !isArray(target[key])) + target[key] = [] + extend(target[key], source[key], deep) + } + else if (source[key] !== undefined) target[key] = source[key] + } + + // Copy all but undefined properties from one or more + // objects to the `target` object. + $.extend = function(target){ + var deep, args = slice.call(arguments, 1) + if (typeof target == 'boolean') { + deep = target + target = args.shift() + } + args.forEach(function(arg){ extend(target, arg, deep) }) + return target + } + + // `$.zepto.qsa` is Zepto's CSS selector implementation which + // uses `document.querySelectorAll` and optimizes for some special cases, like `#id`. + // This method can be overriden in plugins. + zepto.qsa = function(element, selector){ + var found, + maybeID = selector[0] == '#', + maybeClass = !maybeID && selector[0] == '.', + nameOnly = maybeID || maybeClass ? selector.slice(1) : selector, // Ensure that a 1 char tag name still gets checked + isSimple = simpleSelectorRE.test(nameOnly) + return (isDocument(element) && isSimple && maybeID) ? + ( (found = element.getElementById(nameOnly)) ? [found] : [] ) : + (element.nodeType !== 1 && element.nodeType !== 9) ? [] : + slice.call( + isSimple && !maybeID ? + maybeClass ? element.getElementsByClassName(nameOnly) : // If it's simple, it could be a class + element.getElementsByTagName(selector) : // Or a tag + element.querySelectorAll(selector) // Or it's not simple, and we need to query all + ) + } + + function filtered(nodes, selector) { + return selector == null ? $(nodes) : $(nodes).filter(selector) + } + + $.contains = function(parent, node) { + return parent !== node && parent.contains(node) + } + + function funcArg(context, arg, idx, payload) { + return isFunction(arg) ? arg.call(context, idx, payload) : arg + } + + function setAttribute(node, name, value) { + value == null ? node.removeAttribute(name) : node.setAttribute(name, value) + } + + // access className property while respecting SVGAnimatedString + function className(node, value){ + var klass = node.className, + svg = klass && klass.baseVal !== undefined + + if (value === undefined) return svg ? klass.baseVal : klass + svg ? (klass.baseVal = value) : (node.className = value) + } + + // "true" => true + // "false" => false + // "null" => null + // "42" => 42 + // "42.5" => 42.5 + // "08" => "08" + // JSON => parse if valid + // String => self + function deserializeValue(value) { + var num + try { + return value ? + value == "true" || + ( value == "false" ? false : + value == "null" ? null : + !/^0/.test(value) && !isNaN(num = Number(value)) ? num : + /^[\[\{]/.test(value) ? $.parseJSON(value) : + value ) + : value + } catch(e) { + return value + } + } + + $.type = type + $.isFunction = isFunction + $.isWindow = isWindow + $.isArray = isArray + $.isPlainObject = isPlainObject + + $.isEmptyObject = function(obj) { + var name + for (name in obj) return false + return true + } + + $.inArray = function(elem, array, i){ + return emptyArray.indexOf.call(array, elem, i) + } + + $.camelCase = camelize + $.trim = function(str) { + return str == null ? "" : String.prototype.trim.call(str) + } + + // plugin compatibility + $.uuid = 0 + $.support = { } + $.expr = { } + + $.map = function(elements, callback){ + var value, values = [], i, key + if (likeArray(elements)) + for (i = 0; i < elements.length; i++) { + value = callback(elements[i], i) + if (value != null) values.push(value) + } + else + for (key in elements) { + value = callback(elements[key], key) + if (value != null) values.push(value) + } + return flatten(values) + } + + $.each = function(elements, callback){ + var i, key + if (likeArray(elements)) { + for (i = 0; i < elements.length; i++) + if (callback.call(elements[i], i, elements[i]) === false) return elements + } else { + for (key in elements) + if (callback.call(elements[key], key, elements[key]) === false) return elements + } + + return elements + } + + $.grep = function(elements, callback){ + return filter.call(elements, callback) + } + + if (window.JSON) $.parseJSON = JSON.parse + + // Populate the class2type map + $.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase() + }) + + // Define methods that will be available on all + // Zepto collections + $.fn = { + // Because a collection acts like an array + // copy over these useful array functions. + forEach: emptyArray.forEach, + reduce: emptyArray.reduce, + push: emptyArray.push, + sort: emptyArray.sort, + indexOf: emptyArray.indexOf, + concat: emptyArray.concat, + + // `map` and `slice` in the jQuery API work differently + // from their array counterparts + map: function(fn){ + return $($.map(this, function(el, i){ return fn.call(el, i, el) })) + }, + slice: function(){ + return $(slice.apply(this, arguments)) + }, + + ready: function(callback){ + // need to check if document.body exists for IE as that browser reports + // document ready when it hasn't yet created the body element + if (readyRE.test(document.readyState) && document.body) callback($) + else document.addEventListener('DOMContentLoaded', function(){ callback($) }, false) + return this + }, + get: function(idx){ + return idx === undefined ? slice.call(this) : this[idx >= 0 ? idx : idx + this.length] + }, + toArray: function(){ return this.get() }, + size: function(){ + return this.length + }, + remove: function(){ + return this.each(function(){ + if (this.parentNode != null) + this.parentNode.removeChild(this) + }) + }, + each: function(callback){ + emptyArray.every.call(this, function(el, idx){ + return callback.call(el, idx, el) !== false + }) + return this + }, + filter: function(selector){ + if (isFunction(selector)) return this.not(this.not(selector)) + return $(filter.call(this, function(element){ + return zepto.matches(element, selector) + })) + }, + add: function(selector,context){ + return $(uniq(this.concat($(selector,context)))) + }, + is: function(selector){ + return this.length > 0 && zepto.matches(this[0], selector) + }, + not: function(selector){ + var nodes=[] + if (isFunction(selector) && selector.call !== undefined) + this.each(function(idx){ + if (!selector.call(this,idx)) nodes.push(this) + }) + else { + var excludes = typeof selector == 'string' ? this.filter(selector) : + (likeArray(selector) && isFunction(selector.item)) ? slice.call(selector) : $(selector) + this.forEach(function(el){ + if (excludes.indexOf(el) < 0) nodes.push(el) + }) + } + return $(nodes) + }, + has: function(selector){ + return this.filter(function(){ + return isObject(selector) ? + $.contains(this, selector) : + $(this).find(selector).size() + }) + }, + eq: function(idx){ + return idx === -1 ? this.slice(idx) : this.slice(idx, + idx + 1) + }, + first: function(){ + var el = this[0] + return el && !isObject(el) ? el : $(el) + }, + last: function(){ + var el = this[this.length - 1] + return el && !isObject(el) ? el : $(el) + }, + find: function(selector){ + var result, $this = this + if (typeof selector == 'object') + result = $(selector).filter(function(){ + var node = this + return emptyArray.some.call($this, function(parent){ + return $.contains(parent, node) + }) + }) + else if (this.length == 1) result = $(zepto.qsa(this[0], selector)) + else result = this.map(function(){ return zepto.qsa(this, selector) }) + return result + }, + closest: function(selector, context){ + var node = this[0], collection = false + if (typeof selector == 'object') collection = $(selector) + while (node && !(collection ? collection.indexOf(node) >= 0 : zepto.matches(node, selector))) + node = node !== context && !isDocument(node) && node.parentNode + return $(node) + }, + parents: function(selector){ + var ancestors = [], nodes = this + while (nodes.length > 0) + nodes = $.map(nodes, function(node){ + if ((node = node.parentNode) && !isDocument(node) && ancestors.indexOf(node) < 0) { + ancestors.push(node) + return node + } + }) + return filtered(ancestors, selector) + }, + parent: function(selector){ + return filtered(uniq(this.pluck('parentNode')), selector) + }, + children: function(selector){ + return filtered(this.map(function(){ return children(this) }), selector) + }, + contents: function() { + return this.map(function() { return slice.call(this.childNodes) }) + }, + siblings: function(selector){ + return filtered(this.map(function(i, el){ + return filter.call(children(el.parentNode), function(child){ return child!==el }) + }), selector) + }, + empty: function(){ + return this.each(function(){ this.innerHTML = '' }) + }, + // `pluck` is borrowed from Prototype.js + pluck: function(property){ + return $.map(this, function(el){ return el[property] }) + }, + show: function(){ + return this.each(function(){ + this.style.display == "none" && (this.style.display = '') + if (getComputedStyle(this, '').getPropertyValue("display") == "none") + this.style.display = defaultDisplay(this.nodeName) + }) + }, + replaceWith: function(newContent){ + return this.before(newContent).remove() + }, + wrap: function(structure){ + var func = isFunction(structure) + if (this[0] && !func) + var dom = $(structure).get(0), + clone = dom.parentNode || this.length > 1 + + return this.each(function(index){ + $(this).wrapAll( + func ? structure.call(this, index) : + clone ? dom.cloneNode(true) : dom + ) + }) + }, + wrapAll: function(structure){ + if (this[0]) { + $(this[0]).before(structure = $(structure)) + var children + // drill down to the inmost element + while ((children = structure.children()).length) structure = children.first() + $(structure).append(this) + } + return this + }, + wrapInner: function(structure){ + var func = isFunction(structure) + return this.each(function(index){ + var self = $(this), contents = self.contents(), + dom = func ? structure.call(this, index) : structure + contents.length ? contents.wrapAll(dom) : self.append(dom) + }) + }, + unwrap: function(){ + this.parent().each(function(){ + $(this).replaceWith($(this).children()) + }) + return this + }, + clone: function(){ + return this.map(function(){ return this.cloneNode(true) }) + }, + hide: function(){ + return this.css("display", "none") + }, + toggle: function(setting){ + return this.each(function(){ + var el = $(this) + ;(setting === undefined ? el.css("display") == "none" : setting) ? el.show() : el.hide() + }) + }, + prev: function(selector){ return $(this.pluck('previousElementSibling')).filter(selector || '*') }, + next: function(selector){ return $(this.pluck('nextElementSibling')).filter(selector || '*') }, + html: function(html){ + return arguments.length === 0 ? + (this.length > 0 ? this[0].innerHTML : null) : + this.each(function(idx){ + var originHtml = this.innerHTML + $(this).empty().append( funcArg(this, html, idx, originHtml) ) + }) + }, + text: function(text){ + return arguments.length === 0 ? + (this.length > 0 ? this[0].textContent : null) : + this.each(function(){ this.textContent = (text === undefined) ? '' : ''+text }) + }, + attr: function(name, value){ + var result + return (typeof name == 'string' && value === undefined) ? + (this.length == 0 || this[0].nodeType !== 1 ? undefined : + (name == 'value' && this[0].nodeName == 'INPUT') ? this.val() : + (!(result = this[0].getAttribute(name)) && name in this[0]) ? this[0][name] : result + ) : + this.each(function(idx){ + if (this.nodeType !== 1) return + if (isObject(name)) for (key in name) setAttribute(this, key, name[key]) + else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name))) + }) + }, + removeAttr: function(name){ + return this.each(function(){ this.nodeType === 1 && setAttribute(this, name) }) + }, + prop: function(name, value){ + name = propMap[name] || name + return (value === undefined) ? + (this[0] && this[0][name]) : + this.each(function(idx){ + this[name] = funcArg(this, value, idx, this[name]) + }) + }, + data: function(name, value){ + var data = this.attr('data-' + name.replace(capitalRE, '-$1').toLowerCase(), value) + return data !== null ? deserializeValue(data) : undefined + }, + val: function(value){ + return arguments.length === 0 ? + (this[0] && (this[0].multiple ? + $(this[0]).find('option').filter(function(){ return this.selected }).pluck('value') : + this[0].value) + ) : + this.each(function(idx){ + this.value = funcArg(this, value, idx, this.value) + }) + }, + offset: function(coordinates){ + if (coordinates) return this.each(function(index){ + var $this = $(this), + coords = funcArg(this, coordinates, index, $this.offset()), + parentOffset = $this.offsetParent().offset(), + props = { + top: coords.top - parentOffset.top, + left: coords.left - parentOffset.left + } + + if ($this.css('position') == 'static') props['position'] = 'relative' + $this.css(props) + }) + if (this.length==0) return null + var obj = this[0].getBoundingClientRect() + return { + left: obj.left + window.pageXOffset, + top: obj.top + window.pageYOffset, + width: Math.round(obj.width), + height: Math.round(obj.height) + } + }, + css: function(property, value){ + if (arguments.length < 2) { + var element = this[0], computedStyle = getComputedStyle(element, '') + if(!element) return + if (typeof property == 'string') + return element.style[camelize(property)] || computedStyle.getPropertyValue(property) + else if (isArray(property)) { + var props = {} + $.each(isArray(property) ? property: [property], function(_, prop){ + props[prop] = (element.style[camelize(prop)] || computedStyle.getPropertyValue(prop)) + }) + return props + } + } + + var css = '' + if (type(property) == 'string') { + if (!value && value !== 0) + this.each(function(){ this.style.removeProperty(dasherize(property)) }) + else + css = dasherize(property) + ":" + maybeAddPx(property, value) + } else { + for (key in property) + if (!property[key] && property[key] !== 0) + this.each(function(){ this.style.removeProperty(dasherize(key)) }) + else + css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';' + } + + return this.each(function(){ this.style.cssText += ';' + css }) + }, + index: function(element){ + return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0]) + }, + hasClass: function(name){ + if (!name) return false + return emptyArray.some.call(this, function(el){ + return this.test(className(el)) + }, classRE(name)) + }, + addClass: function(name){ + if (!name) return this + return this.each(function(idx){ + classList = [] + var cls = className(this), newName = funcArg(this, name, idx, cls) + newName.split(/\s+/g).forEach(function(klass){ + if (!$(this).hasClass(klass)) classList.push(klass) + }, this) + classList.length && className(this, cls + (cls ? " " : "") + classList.join(" ")) + }) + }, + removeClass: function(name){ + return this.each(function(idx){ + if (name === undefined) return className(this, '') + classList = className(this) + funcArg(this, name, idx, classList).split(/\s+/g).forEach(function(klass){ + classList = classList.replace(classRE(klass), " ") + }) + className(this, classList.trim()) + }) + }, + toggleClass: function(name, when){ + if (!name) return this + return this.each(function(idx){ + var $this = $(this), names = funcArg(this, name, idx, className(this)) + names.split(/\s+/g).forEach(function(klass){ + (when === undefined ? !$this.hasClass(klass) : when) ? + $this.addClass(klass) : $this.removeClass(klass) + }) + }) + }, + scrollTop: function(value){ + if (!this.length) return + var hasScrollTop = 'scrollTop' in this[0] + if (value === undefined) return hasScrollTop ? this[0].scrollTop : this[0].pageYOffset + return this.each(hasScrollTop ? + function(){ this.scrollTop = value } : + function(){ this.scrollTo(this.scrollX, value) }) + }, + scrollLeft: function(value){ + if (!this.length) return + var hasScrollLeft = 'scrollLeft' in this[0] + if (value === undefined) return hasScrollLeft ? this[0].scrollLeft : this[0].pageXOffset + return this.each(hasScrollLeft ? + function(){ this.scrollLeft = value } : + function(){ this.scrollTo(value, this.scrollY) }) + }, + position: function() { + if (!this.length) return + + var elem = this[0], + // Get *real* offsetParent + offsetParent = this.offsetParent(), + // Get correct offsets + offset = this.offset(), + parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset() + + // Subtract element margins + // note: when an element has margin: auto the offsetLeft and marginLeft + // are the same in Safari causing offset.left to incorrectly be 0 + offset.top -= parseFloat( $(elem).css('margin-top') ) || 0 + offset.left -= parseFloat( $(elem).css('margin-left') ) || 0 + + // Add offsetParent borders + parentOffset.top += parseFloat( $(offsetParent[0]).css('border-top-width') ) || 0 + parentOffset.left += parseFloat( $(offsetParent[0]).css('border-left-width') ) || 0 + + // Subtract the two offsets + return { + top: offset.top - parentOffset.top, + left: offset.left - parentOffset.left + } + }, + offsetParent: function() { + return this.map(function(){ + var parent = this.offsetParent || document.body + while (parent && !rootNodeRE.test(parent.nodeName) && $(parent).css("position") == "static") + parent = parent.offsetParent + return parent + }) + } + } + + // for now + $.fn.detach = $.fn.remove + + // Generate the `width` and `height` functions + ;['width', 'height'].forEach(function(dimension){ + var dimensionProperty = + dimension.replace(/./, function(m){ return m[0].toUpperCase() }) + + $.fn[dimension] = function(value){ + var offset, el = this[0] + if (value === undefined) return isWindow(el) ? el['inner' + dimensionProperty] : + isDocument(el) ? el.documentElement['scroll' + dimensionProperty] : + (offset = this.offset()) && offset[dimension] + else return this.each(function(idx){ + el = $(this) + el.css(dimension, funcArg(this, value, idx, el[dimension]())) + }) + } + }) + + function traverseNode(node, fun) { + fun(node) + for (var key in node.childNodes) traverseNode(node.childNodes[key], fun) + } + + // Generate the `after`, `prepend`, `before`, `append`, + // `insertAfter`, `insertBefore`, `appendTo`, and `prependTo` methods. + adjacencyOperators.forEach(function(operator, operatorIndex) { + var inside = operatorIndex % 2 //=> prepend, append + + $.fn[operator] = function(){ + // arguments can be nodes, arrays of nodes, Zepto objects and HTML strings + var argType, nodes = $.map(arguments, function(arg) { + argType = type(arg) + return argType == "object" || argType == "array" || arg == null ? + arg : zepto.fragment(arg) + }), + parent, copyByClone = this.length > 1 + if (nodes.length < 1) return this + + return this.each(function(_, target){ + parent = inside ? target : target.parentNode + + // convert all methods to a "before" operation + target = operatorIndex == 0 ? target.nextSibling : + operatorIndex == 1 ? target.firstChild : + operatorIndex == 2 ? target : + null + + nodes.forEach(function(node){ + if (copyByClone) node = node.cloneNode(true) + else if (!parent) return $(node).remove() + + traverseNode(parent.insertBefore(node, target), function(el){ + if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' && + (!el.type || el.type === 'text/javascript') && !el.src) + window['eval'].call(window, el.innerHTML) + }) + }) + }) + } + + // after => insertAfter + // prepend => prependTo + // before => insertBefore + // append => appendTo + $.fn[inside ? operator+'To' : 'insert'+(operatorIndex ? 'Before' : 'After')] = function(html){ + $(html)[operator](this) + return this + } + }) + + zepto.Z.prototype = $.fn + + // Export internal API functions in the `$.zepto` namespace + zepto.uniq = uniq + zepto.deserializeValue = deserializeValue + $.zepto = zepto + + return $ +})() + +window.Zepto = Zepto +window.$ === undefined && (window.$ = Zepto) + +;(function($){ + var _zid = 1, undefined, + slice = Array.prototype.slice, + isFunction = $.isFunction, + isString = function(obj){ return typeof obj == 'string' }, + handlers = {}, + specialEvents={}, + focusinSupported = 'onfocusin' in window, + focus = { focus: 'focusin', blur: 'focusout' }, + hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' } + + specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents' + + function zid(element) { + return element._zid || (element._zid = _zid++) + } + function findHandlers(element, event, fn, selector) { + event = parse(event) + if (event.ns) var matcher = matcherFor(event.ns) + return (handlers[zid(element)] || []).filter(function(handler) { + return handler + && (!event.e || handler.e == event.e) + && (!event.ns || matcher.test(handler.ns)) + && (!fn || zid(handler.fn) === zid(fn)) + && (!selector || handler.sel == selector) + }) + } + function parse(event) { + var parts = ('' + event).split('.') + return {e: parts[0], ns: parts.slice(1).sort().join(' ')} + } + function matcherFor(ns) { + return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)') + } + + function eventCapture(handler, captureSetting) { + return handler.del && + (!focusinSupported && (handler.e in focus)) || + !!captureSetting + } + + function realEvent(type) { + return hover[type] || (focusinSupported && focus[type]) || type + } + + function add(element, events, fn, data, selector, delegator, capture){ + var id = zid(element), set = (handlers[id] || (handlers[id] = [])) + events.split(/\s/).forEach(function(event){ + if (event == 'ready') return $(document).ready(fn) + var handler = parse(event) + handler.fn = fn + handler.sel = selector + // emulate mouseenter, mouseleave + if (handler.e in hover) fn = function(e){ + var related = e.relatedTarget + if (!related || (related !== this && !$.contains(this, related))) + return handler.fn.apply(this, arguments) + } + handler.del = delegator + var callback = delegator || fn + handler.proxy = function(e){ + e = compatible(e) + if (e.isImmediatePropagationStopped()) return + e.data = data + var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args)) + if (result === false) e.preventDefault(), e.stopPropagation() + return result + } + handler.i = set.length + set.push(handler) + if ('addEventListener' in element) + element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) + }) + } + function remove(element, events, fn, selector, capture){ + var id = zid(element) + ;(events || '').split(/\s/).forEach(function(event){ + findHandlers(element, event, fn, selector).forEach(function(handler){ + delete handlers[id][handler.i] + if ('removeEventListener' in element) + element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) + }) + }) + } + + $.event = { add: add, remove: remove } + + $.proxy = function(fn, context) { + if (isFunction(fn)) { + var proxyFn = function(){ return fn.apply(context, arguments) } + proxyFn._zid = zid(fn) + return proxyFn + } else if (isString(context)) { + return $.proxy(fn[context], fn) + } else { + throw new TypeError("expected function") + } + } + + $.fn.bind = function(event, data, callback){ + return this.on(event, data, callback) + } + $.fn.unbind = function(event, callback){ + return this.off(event, callback) + } + $.fn.one = function(event, selector, data, callback){ + return this.on(event, selector, data, callback, 1) + } + + var returnTrue = function(){return true}, + returnFalse = function(){return false}, + ignoreProperties = /^([A-Z]|returnValue$|layer[XY]$)/, + eventMethods = { + preventDefault: 'isDefaultPrevented', + stopImmediatePropagation: 'isImmediatePropagationStopped', + stopPropagation: 'isPropagationStopped' + } + + function compatible(event, source) { + if (source || !event.isDefaultPrevented) { + source || (source = event) + + $.each(eventMethods, function(name, predicate) { + var sourceMethod = source[name] + event[name] = function(){ + this[predicate] = returnTrue + return sourceMethod && sourceMethod.apply(source, arguments) + } + event[predicate] = returnFalse + }) + + if (source.defaultPrevented !== undefined ? source.defaultPrevented : + 'returnValue' in source ? source.returnValue === false : + source.getPreventDefault && source.getPreventDefault()) + event.isDefaultPrevented = returnTrue + } + return event + } + + function createProxy(event) { + var key, proxy = { originalEvent: event } + for (key in event) + if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key] + + return compatible(proxy, event) + } + + $.fn.delegate = function(selector, event, callback){ + return this.on(event, selector, callback) + } + $.fn.undelegate = function(selector, event, callback){ + return this.off(event, selector, callback) + } + + $.fn.live = function(event, callback){ + $(document.body).delegate(this.selector, event, callback) + return this + } + $.fn.die = function(event, callback){ + $(document.body).undelegate(this.selector, event, callback) + return this + } + + $.fn.on = function(event, selector, data, callback, one){ + var autoRemove, delegator, $this = this + if (event && !isString(event)) { + $.each(event, function(type, fn){ + $this.on(type, selector, data, fn, one) + }) + return $this + } + + if (!isString(selector) && !isFunction(callback) && callback !== false) + callback = data, data = selector, selector = undefined + if (isFunction(data) || data === false) + callback = data, data = undefined + + if (callback === false) callback = returnFalse + + return $this.each(function(_, element){ + if (one) autoRemove = function(e){ + remove(element, e.type, callback) + return callback.apply(this, arguments) + } + + if (selector) delegator = function(e){ + var evt, match = $(e.target).closest(selector, element).get(0) + if (match && match !== element) { + evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element}) + return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1))) + } + } + + add(element, event, callback, data, selector, delegator || autoRemove) + }) + } + $.fn.off = function(event, selector, callback){ + var $this = this + if (event && !isString(event)) { + $.each(event, function(type, fn){ + $this.off(type, selector, fn) + }) + return $this + } + + if (!isString(selector) && !isFunction(callback) && callback !== false) + callback = selector, selector = undefined + + if (callback === false) callback = returnFalse + + return $this.each(function(){ + remove(this, event, callback, selector) + }) + } + + $.fn.trigger = function(event, args){ + event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event) + event._args = args + return this.each(function(){ + // items in the collection might not be DOM elements + if('dispatchEvent' in this) this.dispatchEvent(event) + else $(this).triggerHandler(event, args) + }) + } + + // triggers event handlers on current element just as if an event occurred, + // doesn't trigger an actual event, doesn't bubble + $.fn.triggerHandler = function(event, args){ + var e, result + this.each(function(i, element){ + e = createProxy(isString(event) ? $.Event(event) : event) + e._args = args + e.target = element + $.each(findHandlers(element, event.type || event), function(i, handler){ + result = handler.proxy(e) + if (e.isImmediatePropagationStopped()) return false + }) + }) + return result + } + + // shortcut methods for `.bind(event, fn)` for each event type + ;('focusin focusout load resize scroll unload click dblclick '+ + 'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+ + 'change select keydown keypress keyup error').split(' ').forEach(function(event) { + $.fn[event] = function(callback) { + return callback ? + this.bind(event, callback) : + this.trigger(event) + } + }) + + ;['focus', 'blur'].forEach(function(name) { + $.fn[name] = function(callback) { + if (callback) this.bind(name, callback) + else this.each(function(){ + try { this[name]() } + catch(e) {} + }) + return this + } + }) + + $.Event = function(type, props) { + if (!isString(type)) props = type, type = props.type + var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true + if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name]) + event.initEvent(type, bubbles, true) + return compatible(event) + } + +})(Zepto) + +;(function($){ + var jsonpID = 0, + document = window.document, + key, + name, + rscript = /)<[^<]*)*<\/script>/gi, + scriptTypeRE = /^(?:text|application)\/javascript/i, + xmlTypeRE = /^(?:text|application)\/xml/i, + jsonType = 'application/json', + htmlType = 'text/html', + blankRE = /^\s*$/ + + // trigger a custom event and return false if it was cancelled + function triggerAndReturn(context, eventName, data) { + var event = $.Event(eventName) + $(context).trigger(event, data) + return !event.isDefaultPrevented() + } + + // trigger an Ajax "global" event + function triggerGlobal(settings, context, eventName, data) { + if (settings.global) return triggerAndReturn(context || document, eventName, data) + } + + // Number of active Ajax requests + $.active = 0 + + function ajaxStart(settings) { + if (settings.global && $.active++ === 0) triggerGlobal(settings, null, 'ajaxStart') + } + function ajaxStop(settings) { + if (settings.global && !(--$.active)) triggerGlobal(settings, null, 'ajaxStop') + } + + // triggers an extra global event "ajaxBeforeSend" that's like "ajaxSend" but cancelable + function ajaxBeforeSend(xhr, settings) { + var context = settings.context + if (settings.beforeSend.call(context, xhr, settings) === false || + triggerGlobal(settings, context, 'ajaxBeforeSend', [xhr, settings]) === false) + return false + + triggerGlobal(settings, context, 'ajaxSend', [xhr, settings]) + } + function ajaxSuccess(data, xhr, settings, deferred) { + var context = settings.context, status = 'success' + settings.success.call(context, data, status, xhr) + if (deferred) deferred.resolveWith(context, [data, status, xhr]) + triggerGlobal(settings, context, 'ajaxSuccess', [xhr, settings, data]) + ajaxComplete(status, xhr, settings) + } + // type: "timeout", "error", "abort", "parsererror" + function ajaxError(error, type, xhr, settings, deferred) { + var context = settings.context + settings.error.call(context, xhr, type, error) + if (deferred) deferred.rejectWith(context, [xhr, type, error]) + triggerGlobal(settings, context, 'ajaxError', [xhr, settings, error || type]) + ajaxComplete(type, xhr, settings) + } + // status: "success", "notmodified", "error", "timeout", "abort", "parsererror" + function ajaxComplete(status, xhr, settings) { + var context = settings.context + settings.complete.call(context, xhr, status) + triggerGlobal(settings, context, 'ajaxComplete', [xhr, settings]) + ajaxStop(settings) + } + + // Empty function, used as default callback + function empty() {} + + $.ajaxJSONP = function(options, deferred){ + if (!('type' in options)) return $.ajax(options) + + var _callbackName = options.jsonpCallback, + callbackName = ($.isFunction(_callbackName) ? + _callbackName() : _callbackName) || ('jsonp' + (++jsonpID)), + script = document.createElement('script'), + originalCallback = window[callbackName], + responseData, + abort = function(errorType) { + $(script).triggerHandler('error', errorType || 'abort') + }, + xhr = { abort: abort }, abortTimeout + + if (deferred) deferred.promise(xhr) + + $(script).on('load error', function(e, errorType){ + clearTimeout(abortTimeout) + $(script).off().remove() + + if (e.type == 'error' || !responseData) { + ajaxError(null, errorType || 'error', xhr, options, deferred) + } else { + ajaxSuccess(responseData[0], xhr, options, deferred) + } + + window[callbackName] = originalCallback + if (responseData && $.isFunction(originalCallback)) + originalCallback(responseData[0]) + + originalCallback = responseData = undefined + }) + + if (ajaxBeforeSend(xhr, options) === false) { + abort('abort') + return xhr + } + + window[callbackName] = function(){ + responseData = arguments + } + + script.src = options.url.replace(/\?(.+)=\?/, '?$1=' + callbackName) + document.head.appendChild(script) + + if (options.timeout > 0) abortTimeout = setTimeout(function(){ + abort('timeout') + }, options.timeout) + + return xhr + } + + $.ajaxSettings = { + // Default type of request + type: 'GET', + // Callback that is executed before request + beforeSend: empty, + // Callback that is executed if the request succeeds + success: empty, + // Callback that is executed the the server drops error + error: empty, + // Callback that is executed on request complete (both: error and success) + complete: empty, + // The context for the callbacks + context: null, + // Whether to trigger "global" Ajax events + global: true, + // Transport + xhr: function () { + return new window.XMLHttpRequest() + }, + // MIME types mapping + // IIS returns Javascript as "application/x-javascript" + accepts: { + script: 'text/javascript, application/javascript, application/x-javascript', + json: jsonType, + xml: 'application/xml, text/xml', + html: htmlType, + text: 'text/plain' + }, + // Whether the request is to another domain + crossDomain: false, + // Default timeout + timeout: 0, + // Whether data should be serialized to string + processData: true, + // Whether the browser should be allowed to cache GET responses + cache: true + } + + function mimeToDataType(mime) { + if (mime) mime = mime.split(';', 2)[0] + return mime && ( mime == htmlType ? 'html' : + mime == jsonType ? 'json' : + scriptTypeRE.test(mime) ? 'script' : + xmlTypeRE.test(mime) && 'xml' ) || 'text' + } + + function appendQuery(url, query) { + if (query == '') return url + return (url + '&' + query).replace(/[&?]{1,2}/, '?') + } + + // serialize payload and append it to the URL for GET requests + function serializeData(options) { + if (options.processData && options.data && $.type(options.data) != "string") + options.data = $.param(options.data, options.traditional) + if (options.data && (!options.type || options.type.toUpperCase() == 'GET')) + options.url = appendQuery(options.url, options.data), options.data = undefined + } + + $.ajax = function(options){ + var settings = $.extend({}, options || {}), + deferred = $.Deferred && $.Deferred() + for (key in $.ajaxSettings) if (settings[key] === undefined) settings[key] = $.ajaxSettings[key] + + ajaxStart(settings) + + if (!settings.crossDomain) settings.crossDomain = /^([\w-]+:)?\/\/([^\/]+)/.test(settings.url) && + RegExp.$2 != window.location.host + + if (!settings.url) settings.url = window.location.toString() + serializeData(settings) + if (settings.cache === false) settings.url = appendQuery(settings.url, '_=' + Date.now()) + + var dataType = settings.dataType, hasPlaceholder = /\?.+=\?/.test(settings.url) + if (dataType == 'jsonp' || hasPlaceholder) { + if (!hasPlaceholder) + settings.url = appendQuery(settings.url, + settings.jsonp ? (settings.jsonp + '=?') : settings.jsonp === false ? '' : 'callback=?') + return $.ajaxJSONP(settings, deferred) + } + + var mime = settings.accepts[dataType], + headers = { }, + setHeader = function(name, value) { headers[name.toLowerCase()] = [name, value] }, + protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol, + xhr = settings.xhr(), + nativeSetHeader = xhr.setRequestHeader, + abortTimeout + + if (deferred) deferred.promise(xhr) + + if (!settings.crossDomain) setHeader('X-Requested-With', 'XMLHttpRequest') + setHeader('Accept', mime || '*/*') + if (mime = settings.mimeType || mime) { + if (mime.indexOf(',') > -1) mime = mime.split(',', 2)[0] + xhr.overrideMimeType && xhr.overrideMimeType(mime) + } + if (settings.contentType || (settings.contentType !== false && settings.data && settings.type.toUpperCase() != 'GET')) + setHeader('Content-Type', settings.contentType || 'application/x-www-form-urlencoded') + + if (settings.headers) for (name in settings.headers) setHeader(name, settings.headers[name]) + xhr.setRequestHeader = setHeader + + xhr.onreadystatechange = function(){ + if (xhr.readyState == 4) { + xhr.onreadystatechange = empty + clearTimeout(abortTimeout) + var result, error = false + if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || (xhr.status == 0 && protocol == 'file:')) { + dataType = dataType || mimeToDataType(settings.mimeType || xhr.getResponseHeader('content-type')) + result = xhr.responseText + + try { + // http://perfectionkills.com/global-eval-what-are-the-options/ + if (dataType == 'script') (1,eval)(result) + else if (dataType == 'xml') result = xhr.responseXML + else if (dataType == 'json') result = blankRE.test(result) ? null : $.parseJSON(result) + } catch (e) { error = e } + + if (error) ajaxError(error, 'parsererror', xhr, settings, deferred) + else ajaxSuccess(result, xhr, settings, deferred) + } else { + ajaxError(xhr.statusText || null, xhr.status ? 'error' : 'abort', xhr, settings, deferred) + } + } + } + + if (ajaxBeforeSend(xhr, settings) === false) { + xhr.abort() + ajaxError(null, 'abort', xhr, settings, deferred) + return xhr + } + + if (settings.xhrFields) for (name in settings.xhrFields) xhr[name] = settings.xhrFields[name] + + var async = 'async' in settings ? settings.async : true + xhr.open(settings.type, settings.url, async, settings.username, settings.password) + + for (name in headers) nativeSetHeader.apply(xhr, headers[name]) + + if (settings.timeout > 0) abortTimeout = setTimeout(function(){ + xhr.onreadystatechange = empty + xhr.abort() + ajaxError(null, 'timeout', xhr, settings, deferred) + }, settings.timeout) + + // avoid sending empty string (#319) + xhr.send(settings.data ? settings.data : null) + return xhr + } + + // handle optional data/success arguments + function parseArguments(url, data, success, dataType) { + if ($.isFunction(data)) dataType = success, success = data, data = undefined + if (!$.isFunction(success)) dataType = success, success = undefined + return { + url: url + , data: data + , success: success + , dataType: dataType + } + } + + $.get = function(/* url, data, success, dataType */){ + return $.ajax(parseArguments.apply(null, arguments)) + } + + $.post = function(/* url, data, success, dataType */){ + var options = parseArguments.apply(null, arguments) + options.type = 'POST' + return $.ajax(options) + } + + $.getJSON = function(/* url, data, success */){ + var options = parseArguments.apply(null, arguments) + options.dataType = 'json' + return $.ajax(options) + } + + $.fn.load = function(url, data, success){ + if (!this.length) return this + var self = this, parts = url.split(/\s/), selector, + options = parseArguments(url, data, success), + callback = options.success + if (parts.length > 1) options.url = parts[0], selector = parts[1] + options.success = function(response){ + self.html(selector ? + $('
').html(response.replace(rscript, "")).find(selector) + : response) + callback && callback.apply(self, arguments) + } + $.ajax(options) + return this + } + + var escape = encodeURIComponent + + function serialize(params, obj, traditional, scope){ + var type, array = $.isArray(obj), hash = $.isPlainObject(obj) + $.each(obj, function(key, value) { + type = $.type(value) + if (scope) key = traditional ? scope : + scope + '[' + (hash || type == 'object' || type == 'array' ? key : '') + ']' + // handle data in serializeArray() format + if (!scope && array) params.add(value.name, value.value) + // recurse into nested objects + else if (type == "array" || (!traditional && type == "object")) + serialize(params, value, traditional, key) + else params.add(key, value) + }) + } + + $.param = function(obj, traditional){ + var params = [] + params.add = function(k, v){ this.push(escape(k) + '=' + escape(v)) } + serialize(params, obj, traditional) + return params.join('&').replace(/%20/g, '+') + } +})(Zepto) + +;(function($){ + $.fn.serializeArray = function() { + var result = [], el + $([].slice.call(this.get(0).elements)).each(function(){ + el = $(this) + var type = el.attr('type') + if (this.nodeName.toLowerCase() != 'fieldset' && + !this.disabled && type != 'submit' && type != 'reset' && type != 'button' && + ((type != 'radio' && type != 'checkbox') || this.checked)) + result.push({ + name: el.attr('name'), + value: el.val() + }) + }) + return result + } + + $.fn.serialize = function(){ + var result = [] + this.serializeArray().forEach(function(elm){ + result.push(encodeURIComponent(elm.name) + '=' + encodeURIComponent(elm.value)) + }) + return result.join('&') + } + + $.fn.submit = function(callback) { + if (callback) this.bind('submit', callback) + else if (this.length) { + var event = $.Event('submit') + this.eq(0).trigger(event) + if (!event.isDefaultPrevented()) this.get(0).submit() + } + return this + } + +})(Zepto) + +;(function($, undefined){ + var prefix = '', eventPrefix, endEventName, endAnimationName, + vendors = { Webkit: 'webkit', Moz: '', O: 'o' }, + document = window.document, testEl = document.createElement('div'), + supportedTransforms = /^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i, + transform, + transitionProperty, transitionDuration, transitionTiming, transitionDelay, + animationName, animationDuration, animationTiming, animationDelay, + cssReset = {} + + function dasherize(str) { return str.replace(/([a-z])([A-Z])/, '$1-$2').toLowerCase() } + function normalizeEvent(name) { return eventPrefix ? eventPrefix + name : name.toLowerCase() } + + $.each(vendors, function(vendor, event){ + if (testEl.style[vendor + 'TransitionProperty'] !== undefined) { + prefix = '-' + vendor.toLowerCase() + '-' + eventPrefix = event + return false + } + }) + + transform = prefix + 'transform' + cssReset[transitionProperty = prefix + 'transition-property'] = + cssReset[transitionDuration = prefix + 'transition-duration'] = + cssReset[transitionDelay = prefix + 'transition-delay'] = + cssReset[transitionTiming = prefix + 'transition-timing-function'] = + cssReset[animationName = prefix + 'animation-name'] = + cssReset[animationDuration = prefix + 'animation-duration'] = + cssReset[animationDelay = prefix + 'animation-delay'] = + cssReset[animationTiming = prefix + 'animation-timing-function'] = '' + + $.fx = { + off: (eventPrefix === undefined && testEl.style.transitionProperty === undefined), + speeds: { _default: 400, fast: 200, slow: 600 }, + cssPrefix: prefix, + transitionEnd: normalizeEvent('TransitionEnd'), + animationEnd: normalizeEvent('AnimationEnd') + } + + $.fn.animate = function(properties, duration, ease, callback, delay){ + if ($.isFunction(duration)) + callback = duration, ease = undefined, duration = undefined + if ($.isFunction(ease)) + callback = ease, ease = undefined + if ($.isPlainObject(duration)) + ease = duration.easing, callback = duration.complete, delay = duration.delay, duration = duration.duration + if (duration) duration = (typeof duration == 'number' ? duration : + ($.fx.speeds[duration] || $.fx.speeds._default)) / 1000 + if (delay) delay = parseFloat(delay) / 1000 + return this.anim(properties, duration, ease, callback, delay) + } + + $.fn.anim = function(properties, duration, ease, callback, delay){ + var key, cssValues = {}, cssProperties, transforms = '', + that = this, wrappedCallback, endEvent = $.fx.transitionEnd, + fired = false + + if (duration === undefined) duration = $.fx.speeds._default / 1000 + if (delay === undefined) delay = 0 + if ($.fx.off) duration = 0 + + if (typeof properties == 'string') { + // keyframe animation + cssValues[animationName] = properties + cssValues[animationDuration] = duration + 's' + cssValues[animationDelay] = delay + 's' + cssValues[animationTiming] = (ease || 'linear') + endEvent = $.fx.animationEnd + } else { + cssProperties = [] + // CSS transitions + for (key in properties) + if (supportedTransforms.test(key)) transforms += key + '(' + properties[key] + ') ' + else cssValues[key] = properties[key], cssProperties.push(dasherize(key)) + + if (transforms) cssValues[transform] = transforms, cssProperties.push(transform) + if (duration > 0 && typeof properties === 'object') { + cssValues[transitionProperty] = cssProperties.join(', ') + cssValues[transitionDuration] = duration + 's' + cssValues[transitionDelay] = delay + 's' + cssValues[transitionTiming] = (ease || 'linear') + } + } + + wrappedCallback = function(event){ + if (typeof event !== 'undefined') { + if (event.target !== event.currentTarget) return // makes sure the event didn't bubble from "below" + $(event.target).unbind(endEvent, wrappedCallback) + } else + $(this).unbind(endEvent, wrappedCallback) // triggered by setTimeout + + fired = true + $(this).css(cssReset) + callback && callback.call(this) + } + if (duration > 0){ + this.bind(endEvent, wrappedCallback) + // transitionEnd is not always firing on older Android phones + // so make sure it gets fired + setTimeout(function(){ + if (fired) return + wrappedCallback.call(that) + }, (duration * 1000) + 25) + } + + // trigger page reflow so new elements can animate + this.size() && this.get(0).clientLeft + + this.css(cssValues) + + if (duration <= 0) setTimeout(function() { + that.each(function(){ wrappedCallback.call(this) }) + }, 0) + + return this + } + + testEl = null +})(Zepto) + +;(function($){ + var zepto = $.zepto, oldQsa = zepto.qsa, oldMatches = zepto.matches + + function visible(elem){ + elem = $(elem) + return !!(elem.width() || elem.height()) && elem.css("display") !== "none" + } + + // Implements a subset from: + // http://api.jquery.com/category/selectors/jquery-selector-extensions/ + // + // Each filter function receives the current index, all nodes in the + // considered set, and a value if there were parentheses. The value + // of `this` is the node currently being considered. The function returns the + // resulting node(s), null, or undefined. + // + // Complex selectors are not supported: + // li:has(label:contains("foo")) + li:has(label:contains("bar")) + // ul.inner:first > li + var filters = $.expr[':'] = { + visible: function(){ if (visible(this)) return this }, + hidden: function(){ if (!visible(this)) return this }, + selected: function(){ if (this.selected) return this }, + checked: function(){ if (this.checked) return this }, + parent: function(){ return this.parentNode }, + first: function(idx){ if (idx === 0) return this }, + last: function(idx, nodes){ if (idx === nodes.length - 1) return this }, + eq: function(idx, _, value){ if (idx === value) return this }, + contains: function(idx, _, text){ if ($(this).text().indexOf(text) > -1) return this }, + has: function(idx, _, sel){ if (zepto.qsa(this, sel).length) return this } + } + + var filterRe = new RegExp('(.*):(\\w+)(?:\\(([^)]+)\\))?$\\s*'), + childRe = /^\s*>/, + classTag = 'Zepto' + (+new Date()) + + function process(sel, fn) { + // quote the hash in `a[href^=#]` expression + sel = sel.replace(/=#\]/g, '="#"]') + var filter, arg, match = filterRe.exec(sel) + if (match && match[2] in filters) { + filter = filters[match[2]], arg = match[3] + sel = match[1] + if (arg) { + var num = Number(arg) + if (isNaN(num)) arg = arg.replace(/^["']|["']$/g, '') + else arg = num + } + } + return fn(sel, filter, arg) + } + + zepto.qsa = function(node, selector) { + return process(selector, function(sel, filter, arg){ + try { + var taggedParent + if (!sel && filter) sel = '*' + else if (childRe.test(sel)) + // support "> *" child queries by tagging the parent node with a + // unique class and prepending that classname onto the selector + taggedParent = $(node).addClass(classTag), sel = '.'+classTag+' '+sel + + var nodes = oldQsa(node, sel) + } catch(e) { + console.error('error performing selector: %o', selector) + throw e + } finally { + if (taggedParent) taggedParent.removeClass(classTag) + } + return !filter ? nodes : + zepto.uniq($.map(nodes, function(n, i){ return filter.call(n, i, nodes, arg) })) + }) + } + + zepto.matches = function(node, selector){ + return process(selector, function(sel, filter, arg){ + return (!sel || oldMatches(node, sel)) && + (!filter || filter.call(node, null, arg) === node) + }) + } +})(Zepto) + +;(function($){ + $.fn.end = function(){ + return this.prevObject || $() + } + + $.fn.andSelf = function(){ + return this.add(this.prevObject || $()) + } + + 'filter,add,not,eq,first,last,find,closest,parents,parent,children,siblings'.split(',').forEach(function(property){ + var fn = $.fn[property] + $.fn[property] = function(){ + var ret = fn.apply(this, arguments) + ret.prevObject = this + return ret + } + }) +})(Zepto) + +;(function($){ + var data = {}, dataAttr = $.fn.data, camelize = $.camelCase, + exp = $.expando = 'Zepto' + (+new Date()), emptyArray = [] + + // Get value from node: + // 1. first try key as given, + // 2. then try camelized key, + // 3. fall back to reading "data-*" attribute. + function getData(node, name) { + var id = node[exp], store = id && data[id] + if (name === undefined) return store || setData(node) + else { + if (store) { + if (name in store) return store[name] + var camelName = camelize(name) + if (camelName in store) return store[camelName] + } + return dataAttr.call($(node), name) + } + } + + // Store value under camelized key on node + function setData(node, name, value) { + var id = node[exp] || (node[exp] = ++$.uuid), + store = data[id] || (data[id] = attributeData(node)) + if (name !== undefined) store[camelize(name)] = value + return store + } + + // Read all "data-*" attributes from a node + function attributeData(node) { + var store = {} + $.each(node.attributes || emptyArray, function(i, attr){ + if (attr.name.indexOf('data-') == 0) + store[camelize(attr.name.replace('data-', ''))] = + $.zepto.deserializeValue(attr.value) + }) + return store + } + + $.fn.data = function(name, value) { + return value === undefined ? + // set multiple values via object + $.isPlainObject(name) ? + this.each(function(i, node){ + $.each(name, function(key, value){ setData(node, key, value) }) + }) : + // get value from first element + this.length == 0 ? undefined : getData(this[0], name) : + // set value on all elements + this.each(function(){ setData(this, name, value) }) + } + + $.fn.removeData = function(names) { + if (typeof names == 'string') names = names.split(/\s+/) + return this.each(function(){ + var id = this[exp], store = id && data[id] + if (store) $.each(names || store, function(key){ + delete store[names ? camelize(this) : key] + }) + }) + } + + // Generate extended `remove` and `empty` functions + ;['remove', 'empty'].forEach(function(methodName){ + var origFn = $.fn[methodName] + $.fn[methodName] = function() { + var elements = this.find('*') + if (methodName === 'remove') elements = elements.add(this) + elements.removeData() + return origFn.call(this) + } + }) +})(Zepto) diff --git a/lib/zeptojs/dist/zepto.min.gz b/lib/zeptojs/dist/zepto.min.gz new file mode 100644 index 0000000..b65d8f8 Binary files /dev/null and b/lib/zeptojs/dist/zepto.min.gz differ diff --git a/lib/zeptojs/dist/zepto.min.js b/lib/zeptojs/dist/zepto.min.js new file mode 100644 index 0000000..a20b00f --- /dev/null +++ b/lib/zeptojs/dist/zepto.min.js @@ -0,0 +1,2 @@ +/* Zepto 1.1.3 - zepto event ajax form fx selector stack data - zeptojs.com/license */ +var Zepto=function(){function $(t){return null==t?String(t):j[T.call(t)]||"object"}function L(t){return"function"==$(t)}function Z(t){return null!=t&&t==t.window}function D(t){return null!=t&&t.nodeType==t.DOCUMENT_NODE}function k(t){return"object"==$(t)}function z(t){return k(t)&&!Z(t)&&Object.getPrototypeOf(t)==Object.prototype}function q(t){return"number"==typeof t.length}function R(t){return s.call(t,function(t){return null!=t})}function M(t){return t.length>0?n.fn.concat.apply([],t):t}function F(t){return t.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}function _(t){return t in f?f[t]:f[t]=new RegExp("(^|\\s)"+t+"(\\s|$)")}function H(t,e){return"number"!=typeof e||c[F(t)]?e:e+"px"}function I(t){var e,n;return u[t]||(e=a.createElement(t),a.body.appendChild(e),n=getComputedStyle(e,"").getPropertyValue("display"),e.parentNode.removeChild(e),"none"==n&&(n="block"),u[t]=n),u[t]}function V(t){return"children"in t?o.call(t.children):n.map(t.childNodes,function(t){return 1==t.nodeType?t:void 0})}function U(n,i,r){for(e in i)r&&(z(i[e])||A(i[e]))?(z(i[e])&&!z(n[e])&&(n[e]={}),A(i[e])&&!A(n[e])&&(n[e]=[]),U(n[e],i[e],r)):i[e]!==t&&(n[e]=i[e])}function X(t,e){return null==e?n(t):n(t).filter(e)}function B(t,e,n,i){return L(e)?e.call(t,n,i):e}function J(t,e,n){null==n?t.removeAttribute(e):t.setAttribute(e,n)}function W(e,n){var i=e.className,r=i&&i.baseVal!==t;return n===t?r?i.baseVal:i:void(r?i.baseVal=n:e.className=n)}function Y(t){var e;try{return t?"true"==t||("false"==t?!1:"null"==t?null:/^0/.test(t)||isNaN(e=Number(t))?/^[\[\{]/.test(t)?n.parseJSON(t):t:e):t}catch(i){return t}}function G(t,e){e(t);for(var n in t.childNodes)G(t.childNodes[n],e)}var t,e,n,i,N,S,r=[],o=r.slice,s=r.filter,a=window.document,u={},f={},c={"column-count":1,columns:1,"font-weight":1,"line-height":1,opacity:1,"z-index":1,zoom:1},l=/^\s*<(\w+|!)[^>]*>/,h=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,p=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,d=/^(?:body|html)$/i,m=/([A-Z])/g,v=["val","css","html","text","data","width","height","offset"],g=["after","prepend","before","append"],y=a.createElement("table"),x=a.createElement("tr"),b={tr:a.createElement("tbody"),tbody:y,thead:y,tfoot:y,td:x,th:x,"*":a.createElement("div")},w=/complete|loaded|interactive/,E=/^[\w-]*$/,j={},T=j.toString,C={},O=a.createElement("div"),P={tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},A=Array.isArray||function(t){return t instanceof Array};return C.matches=function(t,e){if(!e||!t||1!==t.nodeType)return!1;var n=t.webkitMatchesSelector||t.mozMatchesSelector||t.oMatchesSelector||t.matchesSelector;if(n)return n.call(t,e);var i,r=t.parentNode,o=!r;return o&&(r=O).appendChild(t),i=~C.qsa(r,e).indexOf(t),o&&O.removeChild(t),i},N=function(t){return t.replace(/-+(.)?/g,function(t,e){return e?e.toUpperCase():""})},S=function(t){return s.call(t,function(e,n){return t.indexOf(e)==n})},C.fragment=function(e,i,r){var s,u,f;return h.test(e)&&(s=n(a.createElement(RegExp.$1))),s||(e.replace&&(e=e.replace(p,"<$1>")),i===t&&(i=l.test(e)&&RegExp.$1),i in b||(i="*"),f=b[i],f.innerHTML=""+e,s=n.each(o.call(f.childNodes),function(){f.removeChild(this)})),z(r)&&(u=n(s),n.each(r,function(t,e){v.indexOf(t)>-1?u[t](e):u.attr(t,e)})),s},C.Z=function(t,e){return t=t||[],t.__proto__=n.fn,t.selector=e||"",t},C.isZ=function(t){return t instanceof C.Z},C.init=function(e,i){var r;if(!e)return C.Z();if("string"==typeof e)if(e=e.trim(),"<"==e[0]&&l.test(e))r=C.fragment(e,RegExp.$1,i),e=null;else{if(i!==t)return n(i).find(e);r=C.qsa(a,e)}else{if(L(e))return n(a).ready(e);if(C.isZ(e))return e;if(A(e))r=R(e);else if(k(e))r=[e],e=null;else if(l.test(e))r=C.fragment(e.trim(),RegExp.$1,i),e=null;else{if(i!==t)return n(i).find(e);r=C.qsa(a,e)}}return C.Z(r,e)},n=function(t,e){return C.init(t,e)},n.extend=function(t){var e,n=o.call(arguments,1);return"boolean"==typeof t&&(e=t,t=n.shift()),n.forEach(function(n){U(t,n,e)}),t},C.qsa=function(t,e){var n,i="#"==e[0],r=!i&&"."==e[0],s=i||r?e.slice(1):e,a=E.test(s);return D(t)&&a&&i?(n=t.getElementById(s))?[n]:[]:1!==t.nodeType&&9!==t.nodeType?[]:o.call(a&&!i?r?t.getElementsByClassName(s):t.getElementsByTagName(e):t.querySelectorAll(e))},n.contains=function(t,e){return t!==e&&t.contains(e)},n.type=$,n.isFunction=L,n.isWindow=Z,n.isArray=A,n.isPlainObject=z,n.isEmptyObject=function(t){var e;for(e in t)return!1;return!0},n.inArray=function(t,e,n){return r.indexOf.call(e,t,n)},n.camelCase=N,n.trim=function(t){return null==t?"":String.prototype.trim.call(t)},n.uuid=0,n.support={},n.expr={},n.map=function(t,e){var n,r,o,i=[];if(q(t))for(r=0;r=0?e:e+this.length]},toArray:function(){return this.get()},size:function(){return this.length},remove:function(){return this.each(function(){null!=this.parentNode&&this.parentNode.removeChild(this)})},each:function(t){return r.every.call(this,function(e,n){return t.call(e,n,e)!==!1}),this},filter:function(t){return L(t)?this.not(this.not(t)):n(s.call(this,function(e){return C.matches(e,t)}))},add:function(t,e){return n(S(this.concat(n(t,e))))},is:function(t){return this.length>0&&C.matches(this[0],t)},not:function(e){var i=[];if(L(e)&&e.call!==t)this.each(function(t){e.call(this,t)||i.push(this)});else{var r="string"==typeof e?this.filter(e):q(e)&&L(e.item)?o.call(e):n(e);this.forEach(function(t){r.indexOf(t)<0&&i.push(t)})}return n(i)},has:function(t){return this.filter(function(){return k(t)?n.contains(this,t):n(this).find(t).size()})},eq:function(t){return-1===t?this.slice(t):this.slice(t,+t+1)},first:function(){var t=this[0];return t&&!k(t)?t:n(t)},last:function(){var t=this[this.length-1];return t&&!k(t)?t:n(t)},find:function(t){var e,i=this;return e="object"==typeof t?n(t).filter(function(){var t=this;return r.some.call(i,function(e){return n.contains(e,t)})}):1==this.length?n(C.qsa(this[0],t)):this.map(function(){return C.qsa(this,t)})},closest:function(t,e){var i=this[0],r=!1;for("object"==typeof t&&(r=n(t));i&&!(r?r.indexOf(i)>=0:C.matches(i,t));)i=i!==e&&!D(i)&&i.parentNode;return n(i)},parents:function(t){for(var e=[],i=this;i.length>0;)i=n.map(i,function(t){return(t=t.parentNode)&&!D(t)&&e.indexOf(t)<0?(e.push(t),t):void 0});return X(e,t)},parent:function(t){return X(S(this.pluck("parentNode")),t)},children:function(t){return X(this.map(function(){return V(this)}),t)},contents:function(){return this.map(function(){return o.call(this.childNodes)})},siblings:function(t){return X(this.map(function(t,e){return s.call(V(e.parentNode),function(t){return t!==e})}),t)},empty:function(){return this.each(function(){this.innerHTML=""})},pluck:function(t){return n.map(this,function(e){return e[t]})},show:function(){return this.each(function(){"none"==this.style.display&&(this.style.display=""),"none"==getComputedStyle(this,"").getPropertyValue("display")&&(this.style.display=I(this.nodeName))})},replaceWith:function(t){return this.before(t).remove()},wrap:function(t){var e=L(t);if(this[0]&&!e)var i=n(t).get(0),r=i.parentNode||this.length>1;return this.each(function(o){n(this).wrapAll(e?t.call(this,o):r?i.cloneNode(!0):i)})},wrapAll:function(t){if(this[0]){n(this[0]).before(t=n(t));for(var e;(e=t.children()).length;)t=e.first();n(t).append(this)}return this},wrapInner:function(t){var e=L(t);return this.each(function(i){var r=n(this),o=r.contents(),s=e?t.call(this,i):t;o.length?o.wrapAll(s):r.append(s)})},unwrap:function(){return this.parent().each(function(){n(this).replaceWith(n(this).children())}),this},clone:function(){return this.map(function(){return this.cloneNode(!0)})},hide:function(){return this.css("display","none")},toggle:function(e){return this.each(function(){var i=n(this);(e===t?"none"==i.css("display"):e)?i.show():i.hide()})},prev:function(t){return n(this.pluck("previousElementSibling")).filter(t||"*")},next:function(t){return n(this.pluck("nextElementSibling")).filter(t||"*")},html:function(t){return 0===arguments.length?this.length>0?this[0].innerHTML:null:this.each(function(e){var i=this.innerHTML;n(this).empty().append(B(this,t,e,i))})},text:function(e){return 0===arguments.length?this.length>0?this[0].textContent:null:this.each(function(){this.textContent=e===t?"":""+e})},attr:function(n,i){var r;return"string"==typeof n&&i===t?0==this.length||1!==this[0].nodeType?t:"value"==n&&"INPUT"==this[0].nodeName?this.val():!(r=this[0].getAttribute(n))&&n in this[0]?this[0][n]:r:this.each(function(t){if(1===this.nodeType)if(k(n))for(e in n)J(this,e,n[e]);else J(this,n,B(this,i,t,this.getAttribute(n)))})},removeAttr:function(t){return this.each(function(){1===this.nodeType&&J(this,t)})},prop:function(e,n){return e=P[e]||e,n===t?this[0]&&this[0][e]:this.each(function(t){this[e]=B(this,n,t,this[e])})},data:function(e,n){var i=this.attr("data-"+e.replace(m,"-$1").toLowerCase(),n);return null!==i?Y(i):t},val:function(t){return 0===arguments.length?this[0]&&(this[0].multiple?n(this[0]).find("option").filter(function(){return this.selected}).pluck("value"):this[0].value):this.each(function(e){this.value=B(this,t,e,this.value)})},offset:function(t){if(t)return this.each(function(e){var i=n(this),r=B(this,t,e,i.offset()),o=i.offsetParent().offset(),s={top:r.top-o.top,left:r.left-o.left};"static"==i.css("position")&&(s.position="relative"),i.css(s)});if(0==this.length)return null;var e=this[0].getBoundingClientRect();return{left:e.left+window.pageXOffset,top:e.top+window.pageYOffset,width:Math.round(e.width),height:Math.round(e.height)}},css:function(t,i){if(arguments.length<2){var r=this[0],o=getComputedStyle(r,"");if(!r)return;if("string"==typeof t)return r.style[N(t)]||o.getPropertyValue(t);if(A(t)){var s={};return n.each(A(t)?t:[t],function(t,e){s[e]=r.style[N(e)]||o.getPropertyValue(e)}),s}}var a="";if("string"==$(t))i||0===i?a=F(t)+":"+H(t,i):this.each(function(){this.style.removeProperty(F(t))});else for(e in t)t[e]||0===t[e]?a+=F(e)+":"+H(e,t[e])+";":this.each(function(){this.style.removeProperty(F(e))});return this.each(function(){this.style.cssText+=";"+a})},index:function(t){return t?this.indexOf(n(t)[0]):this.parent().children().indexOf(this[0])},hasClass:function(t){return t?r.some.call(this,function(t){return this.test(W(t))},_(t)):!1},addClass:function(t){return t?this.each(function(e){i=[];var r=W(this),o=B(this,t,e,r);o.split(/\s+/g).forEach(function(t){n(this).hasClass(t)||i.push(t)},this),i.length&&W(this,r+(r?" ":"")+i.join(" "))}):this},removeClass:function(e){return this.each(function(n){return e===t?W(this,""):(i=W(this),B(this,e,n,i).split(/\s+/g).forEach(function(t){i=i.replace(_(t)," ")}),void W(this,i.trim()))})},toggleClass:function(e,i){return e?this.each(function(r){var o=n(this),s=B(this,e,r,W(this));s.split(/\s+/g).forEach(function(e){(i===t?!o.hasClass(e):i)?o.addClass(e):o.removeClass(e)})}):this},scrollTop:function(e){if(this.length){var n="scrollTop"in this[0];return e===t?n?this[0].scrollTop:this[0].pageYOffset:this.each(n?function(){this.scrollTop=e}:function(){this.scrollTo(this.scrollX,e)})}},scrollLeft:function(e){if(this.length){var n="scrollLeft"in this[0];return e===t?n?this[0].scrollLeft:this[0].pageXOffset:this.each(n?function(){this.scrollLeft=e}:function(){this.scrollTo(e,this.scrollY)})}},position:function(){if(this.length){var t=this[0],e=this.offsetParent(),i=this.offset(),r=d.test(e[0].nodeName)?{top:0,left:0}:e.offset();return i.top-=parseFloat(n(t).css("margin-top"))||0,i.left-=parseFloat(n(t).css("margin-left"))||0,r.top+=parseFloat(n(e[0]).css("border-top-width"))||0,r.left+=parseFloat(n(e[0]).css("border-left-width"))||0,{top:i.top-r.top,left:i.left-r.left}}},offsetParent:function(){return this.map(function(){for(var t=this.offsetParent||a.body;t&&!d.test(t.nodeName)&&"static"==n(t).css("position");)t=t.offsetParent;return t})}},n.fn.detach=n.fn.remove,["width","height"].forEach(function(e){var i=e.replace(/./,function(t){return t[0].toUpperCase()});n.fn[e]=function(r){var o,s=this[0];return r===t?Z(s)?s["inner"+i]:D(s)?s.documentElement["scroll"+i]:(o=this.offset())&&o[e]:this.each(function(t){s=n(this),s.css(e,B(this,r,t,s[e]()))})}}),g.forEach(function(t,e){var i=e%2;n.fn[t]=function(){var t,o,r=n.map(arguments,function(e){return t=$(e),"object"==t||"array"==t||null==e?e:C.fragment(e)}),s=this.length>1;return r.length<1?this:this.each(function(t,a){o=i?a:a.parentNode,a=0==e?a.nextSibling:1==e?a.firstChild:2==e?a:null,r.forEach(function(t){if(s)t=t.cloneNode(!0);else if(!o)return n(t).remove();G(o.insertBefore(t,a),function(t){null==t.nodeName||"SCRIPT"!==t.nodeName.toUpperCase()||t.type&&"text/javascript"!==t.type||t.src||window.eval.call(window,t.innerHTML)})})})},n.fn[i?t+"To":"insert"+(e?"Before":"After")]=function(e){return n(e)[t](this),this}}),C.Z.prototype=n.fn,C.uniq=S,C.deserializeValue=Y,n.zepto=C,n}();window.Zepto=Zepto,void 0===window.$&&(window.$=Zepto),function(t){function l(t){return t._zid||(t._zid=e++)}function h(t,e,n,i){if(e=p(e),e.ns)var r=d(e.ns);return(s[l(t)]||[]).filter(function(t){return!(!t||e.e&&t.e!=e.e||e.ns&&!r.test(t.ns)||n&&l(t.fn)!==l(n)||i&&t.sel!=i)})}function p(t){var e=(""+t).split(".");return{e:e[0],ns:e.slice(1).sort().join(" ")}}function d(t){return new RegExp("(?:^| )"+t.replace(" "," .* ?")+"(?: |$)")}function m(t,e){return t.del&&!u&&t.e in f||!!e}function v(t){return c[t]||u&&f[t]||t}function g(e,i,r,o,a,u,f){var h=l(e),d=s[h]||(s[h]=[]);i.split(/\s/).forEach(function(i){if("ready"==i)return t(document).ready(r);var s=p(i);s.fn=r,s.sel=a,s.e in c&&(r=function(e){var n=e.relatedTarget;return!n||n!==this&&!t.contains(this,n)?s.fn.apply(this,arguments):void 0}),s.del=u;var l=u||r;s.proxy=function(t){if(t=j(t),!t.isImmediatePropagationStopped()){t.data=o;var i=l.apply(e,t._args==n?[t]:[t].concat(t._args));return i===!1&&(t.preventDefault(),t.stopPropagation()),i}},s.i=d.length,d.push(s),"addEventListener"in e&&e.addEventListener(v(s.e),s.proxy,m(s,f))})}function y(t,e,n,i,r){var o=l(t);(e||"").split(/\s/).forEach(function(e){h(t,e,n,i).forEach(function(e){delete s[o][e.i],"removeEventListener"in t&&t.removeEventListener(v(e.e),e.proxy,m(e,r))})})}function j(e,i){return(i||!e.isDefaultPrevented)&&(i||(i=e),t.each(E,function(t,n){var r=i[t];e[t]=function(){return this[n]=x,r&&r.apply(i,arguments)},e[n]=b}),(i.defaultPrevented!==n?i.defaultPrevented:"returnValue"in i?i.returnValue===!1:i.getPreventDefault&&i.getPreventDefault())&&(e.isDefaultPrevented=x)),e}function T(t){var e,i={originalEvent:t};for(e in t)w.test(e)||t[e]===n||(i[e]=t[e]);return j(i,t)}var n,e=1,i=Array.prototype.slice,r=t.isFunction,o=function(t){return"string"==typeof t},s={},a={},u="onfocusin"in window,f={focus:"focusin",blur:"focusout"},c={mouseenter:"mouseover",mouseleave:"mouseout"};a.click=a.mousedown=a.mouseup=a.mousemove="MouseEvents",t.event={add:g,remove:y},t.proxy=function(e,n){if(r(e)){var i=function(){return e.apply(n,arguments)};return i._zid=l(e),i}if(o(n))return t.proxy(e[n],e);throw new TypeError("expected function")},t.fn.bind=function(t,e,n){return this.on(t,e,n)},t.fn.unbind=function(t,e){return this.off(t,e)},t.fn.one=function(t,e,n,i){return this.on(t,e,n,i,1)};var x=function(){return!0},b=function(){return!1},w=/^([A-Z]|returnValue$|layer[XY]$)/,E={preventDefault:"isDefaultPrevented",stopImmediatePropagation:"isImmediatePropagationStopped",stopPropagation:"isPropagationStopped"};t.fn.delegate=function(t,e,n){return this.on(e,t,n)},t.fn.undelegate=function(t,e,n){return this.off(e,t,n)},t.fn.live=function(e,n){return t(document.body).delegate(this.selector,e,n),this},t.fn.die=function(e,n){return t(document.body).undelegate(this.selector,e,n),this},t.fn.on=function(e,s,a,u,f){var c,l,h=this;return e&&!o(e)?(t.each(e,function(t,e){h.on(t,s,a,e,f)}),h):(o(s)||r(u)||u===!1||(u=a,a=s,s=n),(r(a)||a===!1)&&(u=a,a=n),u===!1&&(u=b),h.each(function(n,r){f&&(c=function(t){return y(r,t.type,u),u.apply(this,arguments)}),s&&(l=function(e){var n,o=t(e.target).closest(s,r).get(0);return o&&o!==r?(n=t.extend(T(e),{currentTarget:o,liveFired:r}),(c||u).apply(o,[n].concat(i.call(arguments,1)))):void 0}),g(r,e,u,a,s,l||c)}))},t.fn.off=function(e,i,s){var a=this;return e&&!o(e)?(t.each(e,function(t,e){a.off(t,i,e)}),a):(o(i)||r(s)||s===!1||(s=i,i=n),s===!1&&(s=b),a.each(function(){y(this,e,s,i)}))},t.fn.trigger=function(e,n){return e=o(e)||t.isPlainObject(e)?t.Event(e):j(e),e._args=n,this.each(function(){"dispatchEvent"in this?this.dispatchEvent(e):t(this).triggerHandler(e,n)})},t.fn.triggerHandler=function(e,n){var i,r;return this.each(function(s,a){i=T(o(e)?t.Event(e):e),i._args=n,i.target=a,t.each(h(a,e.type||e),function(t,e){return r=e.proxy(i),i.isImmediatePropagationStopped()?!1:void 0})}),r},"focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select keydown keypress keyup error".split(" ").forEach(function(e){t.fn[e]=function(t){return t?this.bind(e,t):this.trigger(e)}}),["focus","blur"].forEach(function(e){t.fn[e]=function(t){return t?this.bind(e,t):this.each(function(){try{this[e]()}catch(t){}}),this}}),t.Event=function(t,e){o(t)||(e=t,t=e.type);var n=document.createEvent(a[t]||"Events"),i=!0;if(e)for(var r in e)"bubbles"==r?i=!!e[r]:n[r]=e[r];return n.initEvent(t,i,!0),j(n)}}(Zepto),function(t){function l(e,n,i){var r=t.Event(n);return t(e).trigger(r,i),!r.isDefaultPrevented()}function h(t,e,i,r){return t.global?l(e||n,i,r):void 0}function p(e){e.global&&0===t.active++&&h(e,null,"ajaxStart")}function d(e){e.global&&!--t.active&&h(e,null,"ajaxStop")}function m(t,e){var n=e.context;return e.beforeSend.call(n,t,e)===!1||h(e,n,"ajaxBeforeSend",[t,e])===!1?!1:void h(e,n,"ajaxSend",[t,e])}function v(t,e,n,i){var r=n.context,o="success";n.success.call(r,t,o,e),i&&i.resolveWith(r,[t,o,e]),h(n,r,"ajaxSuccess",[e,n,t]),y(o,e,n)}function g(t,e,n,i,r){var o=i.context;i.error.call(o,n,e,t),r&&r.rejectWith(o,[n,e,t]),h(i,o,"ajaxError",[n,i,t||e]),y(e,n,i)}function y(t,e,n){var i=n.context;n.complete.call(i,e,t),h(n,i,"ajaxComplete",[e,n]),d(n)}function x(){}function b(t){return t&&(t=t.split(";",2)[0]),t&&(t==f?"html":t==u?"json":s.test(t)?"script":a.test(t)&&"xml")||"text"}function w(t,e){return""==e?t:(t+"&"+e).replace(/[&?]{1,2}/,"?")}function E(e){e.processData&&e.data&&"string"!=t.type(e.data)&&(e.data=t.param(e.data,e.traditional)),!e.data||e.type&&"GET"!=e.type.toUpperCase()||(e.url=w(e.url,e.data),e.data=void 0)}function j(e,n,i,r){return t.isFunction(n)&&(r=i,i=n,n=void 0),t.isFunction(i)||(r=i,i=void 0),{url:e,data:n,success:i,dataType:r}}function C(e,n,i,r){var o,s=t.isArray(n),a=t.isPlainObject(n);t.each(n,function(n,u){o=t.type(u),r&&(n=i?r:r+"["+(a||"object"==o||"array"==o?n:"")+"]"),!r&&s?e.add(u.name,u.value):"array"==o||!i&&"object"==o?C(e,u,i,n):e.add(n,u)})}var i,r,e=0,n=window.document,o=/)<[^<]*)*<\/script>/gi,s=/^(?:text|application)\/javascript/i,a=/^(?:text|application)\/xml/i,u="application/json",f="text/html",c=/^\s*$/;t.active=0,t.ajaxJSONP=function(i,r){if(!("type"in i))return t.ajax(i);var f,h,o=i.jsonpCallback,s=(t.isFunction(o)?o():o)||"jsonp"+ ++e,a=n.createElement("script"),u=window[s],c=function(e){t(a).triggerHandler("error",e||"abort")},l={abort:c};return r&&r.promise(l),t(a).on("load error",function(e,n){clearTimeout(h),t(a).off().remove(),"error"!=e.type&&f?v(f[0],l,i,r):g(null,n||"error",l,i,r),window[s]=u,f&&t.isFunction(u)&&u(f[0]),u=f=void 0}),m(l,i)===!1?(c("abort"),l):(window[s]=function(){f=arguments},a.src=i.url.replace(/\?(.+)=\?/,"?$1="+s),n.head.appendChild(a),i.timeout>0&&(h=setTimeout(function(){c("timeout")},i.timeout)),l)},t.ajaxSettings={type:"GET",beforeSend:x,success:x,error:x,complete:x,context:null,global:!0,xhr:function(){return new window.XMLHttpRequest},accepts:{script:"text/javascript, application/javascript, application/x-javascript",json:u,xml:"application/xml, text/xml",html:f,text:"text/plain"},crossDomain:!1,timeout:0,processData:!0,cache:!0},t.ajax=function(e){var n=t.extend({},e||{}),o=t.Deferred&&t.Deferred();for(i in t.ajaxSettings)void 0===n[i]&&(n[i]=t.ajaxSettings[i]);p(n),n.crossDomain||(n.crossDomain=/^([\w-]+:)?\/\/([^\/]+)/.test(n.url)&&RegExp.$2!=window.location.host),n.url||(n.url=window.location.toString()),E(n),n.cache===!1&&(n.url=w(n.url,"_="+Date.now()));var s=n.dataType,a=/\?.+=\?/.test(n.url);if("jsonp"==s||a)return a||(n.url=w(n.url,n.jsonp?n.jsonp+"=?":n.jsonp===!1?"":"callback=?")),t.ajaxJSONP(n,o);var j,u=n.accepts[s],f={},l=function(t,e){f[t.toLowerCase()]=[t,e]},h=/^([\w-]+:)\/\//.test(n.url)?RegExp.$1:window.location.protocol,d=n.xhr(),y=d.setRequestHeader;if(o&&o.promise(d),n.crossDomain||l("X-Requested-With","XMLHttpRequest"),l("Accept",u||"*/*"),(u=n.mimeType||u)&&(u.indexOf(",")>-1&&(u=u.split(",",2)[0]),d.overrideMimeType&&d.overrideMimeType(u)),(n.contentType||n.contentType!==!1&&n.data&&"GET"!=n.type.toUpperCase())&&l("Content-Type",n.contentType||"application/x-www-form-urlencoded"),n.headers)for(r in n.headers)l(r,n.headers[r]);if(d.setRequestHeader=l,d.onreadystatechange=function(){if(4==d.readyState){d.onreadystatechange=x,clearTimeout(j);var e,i=!1;if(d.status>=200&&d.status<300||304==d.status||0==d.status&&"file:"==h){s=s||b(n.mimeType||d.getResponseHeader("content-type")),e=d.responseText;try{"script"==s?(1,eval)(e):"xml"==s?e=d.responseXML:"json"==s&&(e=c.test(e)?null:t.parseJSON(e))}catch(r){i=r}i?g(i,"parsererror",d,n,o):v(e,d,n,o)}else g(d.statusText||null,d.status?"error":"abort",d,n,o)}},m(d,n)===!1)return d.abort(),g(null,"abort",d,n,o),d;if(n.xhrFields)for(r in n.xhrFields)d[r]=n.xhrFields[r];var T="async"in n?n.async:!0;d.open(n.type,n.url,T,n.username,n.password);for(r in f)y.apply(d,f[r]);return n.timeout>0&&(j=setTimeout(function(){d.onreadystatechange=x,d.abort(),g(null,"timeout",d,n,o)},n.timeout)),d.send(n.data?n.data:null),d},t.get=function(){return t.ajax(j.apply(null,arguments))},t.post=function(){var e=j.apply(null,arguments);return e.type="POST",t.ajax(e)},t.getJSON=function(){var e=j.apply(null,arguments);return e.dataType="json",t.ajax(e)},t.fn.load=function(e,n,i){if(!this.length)return this;var a,r=this,s=e.split(/\s/),u=j(e,n,i),f=u.success;return s.length>1&&(u.url=s[0],a=s[1]),u.success=function(e){r.html(a?t("
").html(e.replace(o,"")).find(a):e),f&&f.apply(r,arguments)},t.ajax(u),this};var T=encodeURIComponent;t.param=function(t,e){var n=[];return n.add=function(t,e){this.push(T(t)+"="+T(e))},C(n,t,e),n.join("&").replace(/%20/g,"+")}}(Zepto),function(t){t.fn.serializeArray=function(){var n,e=[];return t([].slice.call(this.get(0).elements)).each(function(){n=t(this);var i=n.attr("type");"fieldset"!=this.nodeName.toLowerCase()&&!this.disabled&&"submit"!=i&&"reset"!=i&&"button"!=i&&("radio"!=i&&"checkbox"!=i||this.checked)&&e.push({name:n.attr("name"),value:n.val()})}),e},t.fn.serialize=function(){var t=[];return this.serializeArray().forEach(function(e){t.push(encodeURIComponent(e.name)+"="+encodeURIComponent(e.value))}),t.join("&")},t.fn.submit=function(e){if(e)this.bind("submit",e);else if(this.length){var n=t.Event("submit");this.eq(0).trigger(n),n.isDefaultPrevented()||this.get(0).submit()}return this}}(Zepto),function(t,e){function b(t){return t.replace(/([a-z])([A-Z])/,"$1-$2").toLowerCase()}function w(t){return i?i+t:t.toLowerCase()}var i,c,l,h,p,d,m,v,g,y,n="",s={Webkit:"webkit",Moz:"",O:"o"},a=window.document,u=a.createElement("div"),f=/^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i,x={};t.each(s,function(t,r){return u.style[t+"TransitionProperty"]!==e?(n="-"+t.toLowerCase()+"-",i=r,!1):void 0}),c=n+"transform",x[l=n+"transition-property"]=x[h=n+"transition-duration"]=x[d=n+"transition-delay"]=x[p=n+"transition-timing-function"]=x[m=n+"animation-name"]=x[v=n+"animation-duration"]=x[y=n+"animation-delay"]=x[g=n+"animation-timing-function"]="",t.fx={off:i===e&&u.style.transitionProperty===e,speeds:{_default:400,fast:200,slow:600},cssPrefix:n,transitionEnd:w("TransitionEnd"),animationEnd:w("AnimationEnd")},t.fn.animate=function(n,i,r,o,s){return t.isFunction(i)&&(o=i,r=e,i=e),t.isFunction(r)&&(o=r,r=e),t.isPlainObject(i)&&(r=i.easing,o=i.complete,s=i.delay,i=i.duration),i&&(i=("number"==typeof i?i:t.fx.speeds[i]||t.fx.speeds._default)/1e3),s&&(s=parseFloat(s)/1e3),this.anim(n,i,r,o,s)},t.fn.anim=function(n,i,r,o,s){var a,w,T,u={},E="",j=this,C=t.fx.transitionEnd,N=!1;if(i===e&&(i=t.fx.speeds._default/1e3),s===e&&(s=0),t.fx.off&&(i=0),"string"==typeof n)u[m]=n,u[v]=i+"s",u[y]=s+"s",u[g]=r||"linear",C=t.fx.animationEnd;else{w=[];for(a in n)f.test(a)?E+=a+"("+n[a]+") ":(u[a]=n[a],w.push(b(a)));E&&(u[c]=E,w.push(c)),i>0&&"object"==typeof n&&(u[l]=w.join(", "),u[h]=i+"s",u[d]=s+"s",u[p]=r||"linear")}return T=function(e){if("undefined"!=typeof e){if(e.target!==e.currentTarget)return;t(e.target).unbind(C,T)}else t(this).unbind(C,T);N=!0,t(this).css(x),o&&o.call(this)},i>0&&(this.bind(C,T),setTimeout(function(){N||T.call(j)},1e3*i+25)),this.size()&&this.get(0).clientLeft,this.css(u),0>=i&&setTimeout(function(){j.each(function(){T.call(this)})},0),this},u=null}(Zepto),function(t){function r(e){return e=t(e),!(!e.width()&&!e.height())&&"none"!==e.css("display")}function f(t,e){t=t.replace(/=#\]/g,'="#"]');var n,i,r=s.exec(t);if(r&&r[2]in o&&(n=o[r[2]],i=r[3],t=r[1],i)){var a=Number(i);i=isNaN(a)?i.replace(/^["']|["']$/g,""):a}return e(t,n,i)}var e=t.zepto,n=e.qsa,i=e.matches,o=t.expr[":"]={visible:function(){return r(this)?this:void 0},hidden:function(){return r(this)?void 0:this},selected:function(){return this.selected?this:void 0},checked:function(){return this.checked?this:void 0},parent:function(){return this.parentNode},first:function(t){return 0===t?this:void 0},last:function(t,e){return t===e.length-1?this:void 0},eq:function(t,e,n){return t===n?this:void 0},contains:function(e,n,i){return t(this).text().indexOf(i)>-1?this:void 0},has:function(t,n,i){return e.qsa(this,i).length?this:void 0}},s=new RegExp("(.*):(\\w+)(?:\\(([^)]+)\\))?$\\s*"),a=/^\s*>/,u="Zepto"+ +new Date;e.qsa=function(i,r){return f(r,function(o,s,f){try{var c;!o&&s?o="*":a.test(o)&&(c=t(i).addClass(u),o="."+u+" "+o);var l=n(i,o)}catch(h){throw console.error("error performing selector: %o",r),h}finally{c&&c.removeClass(u)}return s?e.uniq(t.map(l,function(t,e){return s.call(t,e,l,f)})):l})},e.matches=function(t,e){return f(e,function(e,n,r){return!(e&&!i(t,e)||n&&n.call(t,null,r)!==t)})}}(Zepto),function(t){t.fn.end=function(){return this.prevObject||t()},t.fn.andSelf=function(){return this.add(this.prevObject||t())},"filter,add,not,eq,first,last,find,closest,parents,parent,children,siblings".split(",").forEach(function(e){var n=t.fn[e];t.fn[e]=function(){var t=n.apply(this,arguments);return t.prevObject=this,t}})}(Zepto),function(t){function s(o,s){var u=o[r],f=u&&e[u];if(void 0===s)return f||a(o);if(f){if(s in f)return f[s];var c=i(s);if(c in f)return f[c]}return n.call(t(o),s)}function a(n,o,s){var a=n[r]||(n[r]=++t.uuid),f=e[a]||(e[a]=u(n));return void 0!==o&&(f[i(o)]=s),f}function u(e){var n={};return t.each(e.attributes||o,function(e,r){0==r.name.indexOf("data-")&&(n[i(r.name.replace("data-",""))]=t.zepto.deserializeValue(r.value))}),n}var e={},n=t.fn.data,i=t.camelCase,r=t.expando="Zepto"+ +new Date,o=[];t.fn.data=function(e,n){return void 0===n?t.isPlainObject(e)?this.each(function(n,i){t.each(e,function(t,e){a(i,t,e)})}):0==this.length?void 0:s(this[0],e):this.each(function(){a(this,e,n)})},t.fn.removeData=function(n){return"string"==typeof n&&(n=n.split(/\s+/)),this.each(function(){var o=this[r],s=o&&e[o];s&&t.each(n||s,function(t){delete s[n?i(this):t]})})},["remove","empty"].forEach(function(e){var n=t.fn[e];t.fn[e]=function(){var t=this.find("*");return"remove"===e&&(t=t.add(this)),t.removeData(),n.call(this)}})}(Zepto); \ No newline at end of file diff --git a/lib/zeptojs/examples/anim.html b/lib/zeptojs/examples/anim.html new file mode 100644 index 0000000..e30193e --- /dev/null +++ b/lib/zeptojs/examples/anim.html @@ -0,0 +1,9 @@ +
HELLO WORLD
+ + + + + + diff --git a/lib/zeptojs/examples/iphone/index.html b/lib/zeptojs/examples/iphone/index.html new file mode 100644 index 0000000..10a9bc6 --- /dev/null +++ b/lib/zeptojs/examples/iphone/index.html @@ -0,0 +1,57 @@ + + + + iPhone.Zepto + + + + + + + + + + + + + + diff --git a/lib/zeptojs/examples/iphone/iphone.css b/lib/zeptojs/examples/iphone/iphone.css new file mode 100644 index 0000000..1690361 --- /dev/null +++ b/lib/zeptojs/examples/iphone/iphone.css @@ -0,0 +1,161 @@ +* { + margin: 0px; + padding: 0px; +} +.toolbar { + -webkit-box-sizing: border-box; + background: #6D84A2 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAArCAIAAAA2QHWOAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAEpJREFUeNpNjCEOgEAQA5v9/9eQaAQCd57L0WXTDSmimXZEse1HnNcIIINZYTPVv7Ac4/EWe7OTsC/ec+nDgcj/dpcH7EXt8up4AfRWcOjLIqWFAAAAAElFTkSuQmCC) repeat-x; + border-bottom: 1px solid #2D3642; + height: 45px; + padding: 10px; + position: relative; +} +body { + -webkit-perspective: 800; + -webkit-text-size-adjust: none; + -webkit-transform-style: preserve-3d; + -webkit-user-select: none; + font-family: Helvetica; + overflow-x: hidden; + height: 100%; + width: 100%; +} + +body > section { + background: #C5CCD3 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAABCAIAAACdaSOZAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABdJREFUeNpiPHrmCgMC/GNjYwNSAAEGADdNA3dnzPlQAAAAAElFTkSuQmCC); + display: block; + left: 0px; + position: absolute; + width: 100%; + -webkit-transform: translate3d(100%,0%,0%); + opacity: 0; + -webkit-transition: all 0.25s ease-in-out; + -webkit-transform-style: preserve-3d; + z-index: -10; +} + +.reverse { + -webkit-transform: translate3d(-100%, 0%, 0%); + opacity: 0; +} + +.current { + -webkit-transform: translate3d(0%,0%, 0%); + opacity: 1; + z-index: 1; +} +h1, h2 { + color: #4C566C; + font: normal normal bold 18px/normal Helvetica; + margin: 10px 20px 6px; + text-shadow: rgba(255, 255, 255, 0.19) +} +body.landscape .toolbar > h1 { + margin-left: -125px; + width: 250px; +} + +.toolbar > h1 { + color: white; + font-size: 20px; + font-weight: bold; + height: 40px; + left: 50%; + line-height: 1em; + margin: 1px 0px 0px -75px; + overflow: hidden; + position: absolute; + text-align: center; + text-overflow: ellipsis; + text-shadow: rgba(0, 0, 0, 0.398438) 0px -1px 0px; + top: 10px; + white-space: nowrap; + width: 150px; +} + +ul { + background: white; + border: 1px solid #B4B4B4; + border-bottom-left-radius: 8px 8px; + border-bottom-right-radius: 8px 8px; + border-top-left-radius: 8px 8px; + border-top-right-radius: 8px 8px; + color: black; + font: normal normal bold 17px/normal Helvetica; + margin: 15px 10px 17px; + padding: 0px; +} + +ul li { + color: black; + font-size: 14px; + border-top: 1px solid #B4B4B4; + color: #666; + list-style-type: none; + padding: 10px; +} + +li:first-child, li:first-child a { + border-top: 0px; + border-top-left-radius: 8px 8px; + border-top-right-radius: 8px 8px; +} + +ul li.arrow { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAUCAYAAAB4d5a9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAKVJREFUeNpi/P//PwOtARMDHcDwsYQFRJSXl8P4dVC6CZvizs5O8i1BsqARid9Ei+BiQ2KDLKumhSU1QNyKxG+hlkXoEQ+yqAPNogpapK5KNIvaKbUIVxKeAsTvkPg5QCxETUukgfgAkqFPgdgBzVKKLIFZoIJmwR1qBRdNLEC2BJQpV9LCAmRL/gBxAtRwqlqAXqzcgRrOQE0LQIBxtNIiBQAEGAA7xCa2yF9zEgAAAABJRU5ErkJggg==); + background-position: 100% 50%; + background-repeat: no-repeat; +} + +body > .current { + display: block !important; +} + +body.landscape > * { + min-height: 320px; +} + + +ul li a, li.img a + a { + color: black; + display: block; + margin: -10px; + overflow: hidden; + padding: 12px 10px; + text-decoration: none; + text-overflow: ellipsis; + white-space: nowrap; +} + + +.button, .back, .cancel, .add { +-webkit-border-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAeCAIAAACqmwlGAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAatJREFUeNqVlMtKw0AUhqfJSRpTk5Za1FAvxS6sgrVeFrpQdOcT+IpuBVHcuVBQpCDYjeAdkSpavDTGNJkz43FpFZz8DISZ+b75J5tJbWwfMsZuby7rh/vN5v1H+539TMZxPa84v7g0WirTFISQR/t7jdOTcqVaGp+ze50uIfDbL62H3a3NqerMwtIqXF9dnF+dL6+t9/S6hmFqut4luHns80aKpcrxwY43PKK7+f6J2ZVcYTCl6ZIxKuwatEhbpmU72UKjfgCakXFz/TwW7L8QlgIbTNtBwVAgU0g644IGZoycqYVgQGRRLBQFggEpHJUFBI4iUhYIBhSSPooCwUB0zEWiBsERlRsECJSIUlEgOHmDH3TM909FgWBgqe8oCgRrLGGADlcvIDB5A2nfQ7kD6D5J/jn5lZILBoAUQgWVUgDoWi5r8zhUEXgU5nOOVp0sB+0W+f8WEDY9MabpYNUqQ29Pd1Hoi79eA1qkLQIIY5CGIOwUvYHBQr5xdvn4/BSGnS7BstIDhexUbVo3jNarD1Ky5xfftszF+WqPZcKvp5IjfoYRoYHv0/QLHvXjAb8xkEMAAAAASUVORK5CYII=) 0 5 0 5 stretch stretch; +background: none; +border-width: 0px 5px; +color: white; +font-family: inherit; +font-size: 12px; +font-weight: bold; +height: 30px; +line-height: 30px; +margin: 0px; +overflow: ; +padding: 0px 3px; +position: absolute; +right: 6px; +text-decoration: none; +text-overflow: ellipsis; +text-shadow: rgba(0, 0, 0, 0.496094) 0px -1px 0px; +top: 8px; +white-space: nowrap; +width: auto; +} + +.back { + -webkit-border-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACsAAAAeCAIAAAA6iHCJAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAArFJREFUeNrF0mtPE2EQBeB5t9vSRqEUKiAFkasggmC5x3CRECHx8sH/qMHEoKkiMUQTNJIIaAjeiFGUcEsotN22u9vtOzMKogYL39rlyfkBk3NG3HsyC8dLJhOzM8+/fvkcjexAtghRXVN/dXCkvOIcAKhEDMdY+7ESejgeqKpv7x3J9/ogS5hoc33l0YPxju6+YFefSkRwlI311cnQRM/gbd+ZUpfb43CokD2nCv2lFbVvZib9JaUqMUEGTYtNPZ7oHLpZXBJQFIUYSCJkj3A4830lrd3Di2/nVcxYwTSMift3m4LXCovKCYGQIDe8vrJveNQfTE+Fys9f8pdVSSTILeFyF/z/B8uflnSLG+qakRlRQo45XO5DHejJxOtXLzsG7yA7ME2Qe0hw6A+mn4ZqLvY4XR6UCLZAxH8rfFx6Z6BaXVJlSQS7SKSDC+JabGFurrnvBvJ+NXZBYpX4oP9AQ5eietKSAMDuDt4vLkhHfr6/XCKCvZBIjezuLMzPN/XcIhIADPYiZPXFs8lAQ7dwOA8KsL8Dr7/CoDzSDDgJCT2ljI0OOzBGKMVJACGUXU2/3h/Uo5vMDCdBQSRLcn9nox7bEgLsDoDCDKYli4p89ZXFZjJ6Ah3wvlhcb2mqPa1a0jIBhJ1RiPl3wpH4QG+bTGwDo51DKPwHMccS5mBvSzyyCcB2rgB/k5aoqK72xgo9FrbxgsMSulkZOBvwu1O6BjnGhO48p8IZfj1EsPWCR+jSMiCX0pbh9xXsrZCZrW1toOcy6mFMpyA3mNnUwi2NtQoxZwaZwzF9bKhTpHaN+C7KNGQPobTMhBZe7Wyts1iox729RNyOJkeHOj4sf19Z3YgmsraI251X6ve2BYMEIqIlVebjd0rj2la0rrrySkuDU1UhSySiYVrbkbiZ2qv2J5+53J2yzo3SAAAAAElFTkSuQmCC) 0 8 0 14 stretch stretch; +border-width: 0px 8px 0px 14px; +left: 6px; +max-width: 55px; +padding: 0px; +right: auto; +} diff --git a/lib/zeptojs/examples/load_jquery_on_ie.html b/lib/zeptojs/examples/load_jquery_on_ie.html new file mode 100644 index 0000000..8c51d74 --- /dev/null +++ b/lib/zeptojs/examples/load_jquery_on_ie.html @@ -0,0 +1,18 @@ + + + + Load jQuery if Zepto is not supported + + +

Load jQuery if Zepto is not supported

+ + + + diff --git a/lib/zeptojs/examples/snow/index.html b/lib/zeptojs/examples/snow/index.html new file mode 100644 index 0000000..8941004 --- /dev/null +++ b/lib/zeptojs/examples/snow/index.html @@ -0,0 +1,51 @@ + + + + Let it snow with Zepto.js + + + + + + + + + + + + + + + + diff --git a/lib/zeptojs/examples/touch_events.html b/lib/zeptojs/examples/touch_events.html new file mode 100644 index 0000000..3a5eadf --- /dev/null +++ b/lib/zeptojs/examples/touch_events.html @@ -0,0 +1,30 @@ + + + + + + + + +
    +
  • List item 1 DELETE
  • +
  • List item 2 DELETE
  • +
  • List item 3 DELETE
  • +
  • List item 4 DELETE
  • +
+ + diff --git a/lib/zeptojs/make b/lib/zeptojs/make new file mode 100755 index 0000000..d9254ee --- /dev/null +++ b/lib/zeptojs/make @@ -0,0 +1,98 @@ +#!/usr/bin/env coffee +require 'shelljs/make' +fs = require 'fs' + +version = '1.1.3' +zepto_js = 'dist/zepto.js' +zepto_min = 'dist/zepto.min.js' +zepto_gz = 'dist/zepto.min.gz' + +port = 3999 +root = __dirname + '/' + +target.all = -> + target[zepto_js]() + target.test() + +## TASKS ## + +target.test = -> + test_app = require './test/server' + server = test_app.listen port + exec "phantomjs --disk-cache=true test/runner.coffee 'http://localhost:#{port}/'", (code) -> + server.close -> exit(code) + +target[zepto_js] = -> + target.build() unless test('-e', zepto_js) + +target[zepto_min] = -> + target.minify() if stale(zepto_min, zepto_js) + +target[zepto_gz] = -> + target.compress() if stale(zepto_gz, zepto_min) + +target.dist = -> + target.build() + target.minify() + target.compress() + +target.build = -> + cd __dirname + mkdir '-p', 'dist' + modules = (env['MODULES'] || 'zepto event ajax form ie').split(' ') + module_files = ( "src/#{module}.js" for module in modules ) + intro = "/* Zepto #{describe_version()} - #{modules.join(' ')} - zeptojs.com/license */\n" + dist = intro + cat(module_files).replace(/^\/[\/*].*$/mg, '').replace(/\n{3,}/g, "\n\n") + dist.to(zepto_js) + report_size(zepto_js) + +target.minify = -> + target.build() unless test('-e', zepto_js) + zepto_code = cat(zepto_js) + intro = zepto_code.slice(0, zepto_code.indexOf("\n") + 1) + (intro + minify(zepto_code)).to(zepto_min) + report_size(zepto_min) + +target.compress = -> + gzip = require('zlib').createGzip() + inp = fs.createReadStream(zepto_min) + out = fs.createWriteStream(zepto_gz) + inp.pipe(gzip).pipe(out) + out.on 'close', -> + report_size(zepto_gz) + factor = fsize(zepto_js) / fsize(zepto_gz) + echo "compression factor: #{format_number(factor)}" + +## HELPERS ## + +stale = (file, source) -> + target[source]() + !test('-e', file) || mtime(file) < mtime(source) + +mtime = (file) -> + fs.statSync(file).mtime.getTime() + +fsize = (file) -> + fs.statSync(file).size + +format_number = (size, precision = 1) -> + factor = Math.pow(10, precision) + decimal = Math.round(size * factor) % factor + parseInt(size) + "." + decimal + +report_size = (file) -> + echo "#{file}: #{format_number(fsize(file) / 1024)} KiB" + +describe_version = -> + desc = exec "git --git-dir='#{root + '.git'}' describe --tags HEAD", silent: true + if desc.code is 0 then desc.output.replace(/\s+$/, '') else version + +minify = (source_code) -> + uglify = require('uglify-js') + compressor = uglify.Compressor() + ast = uglify.parse(source_code) + ast.figure_out_scope() + ast.compute_char_frequency(); + ast.mangle_names(); + ast = ast.transform(compressor) + return ast.print_to_string() diff --git a/lib/zeptojs/package.json b/lib/zeptojs/package.json new file mode 100644 index 0000000..c89f50e --- /dev/null +++ b/lib/zeptojs/package.json @@ -0,0 +1,26 @@ +{ + "name": "zepto" + , "version": "1.1.3" + , "homepage": "http://zeptojs.com" + , "scripts": { + "test": "coffee make test" + , "dist": "coffee make dist" + , "start": "coffee test/server.coffee" + } + , "repository": { + "type": "git" + , "url": "https://github.com/madrobby/zepto.git" + } + , "licenses": [ + { + "type": "MIT" + , "url": "http://zeptojs.com/license/" + } + ] + , "devDependencies": { + "uglify-js": "2.4.x" + , "express": "3.1.x" + , "coffee-script": "1.5.x" + , "shelljs": "0.1.x" + } +} diff --git a/lib/zeptojs/script/bootstrap b/lib/zeptojs/script/bootstrap new file mode 100755 index 0000000..bd84bb1 --- /dev/null +++ b/lib/zeptojs/script/bootstrap @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -e + +npm install + +if phantom_version="$(phantomjs --version)"; then + echo "phantomjs ${phantom_version}" +else + echo "You should install PhantomJS for \`script/test' to work." >&2 +fi diff --git a/lib/zeptojs/script/guard b/lib/zeptojs/script/guard new file mode 100755 index 0000000..93fe00e --- /dev/null +++ b/lib/zeptojs/script/guard @@ -0,0 +1,6 @@ +#!/bin/sh +if [ -z "$BUNDLE_GEMFILE" ]; then + export BUNDLE_GEMFILE=shutup +fi + +exec guard --no-notify "$@" diff --git a/lib/zeptojs/script/test b/lib/zeptojs/script/test new file mode 100755 index 0000000..2d76bc5 --- /dev/null +++ b/lib/zeptojs/script/test @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +port=3999 + +export PATH=node_modules/.bin:"$PATH" + +coffee test/server.coffee $port & +pid=$! + +terminate_test_server() { + kill $pid +} +trap terminate_test_server EXIT + +phantomjs --disk-cache=true \ + test/runner.coffee "http://localhost:${port}/" "$@" \ + 2> >(grep -v "CoreText performance note" >&2) + +exit $? diff --git a/lib/zeptojs/src/ajax.js b/lib/zeptojs/src/ajax.js new file mode 100644 index 0000000..1402c5a --- /dev/null +++ b/lib/zeptojs/src/ajax.js @@ -0,0 +1,344 @@ +// Zepto.js +// (c) 2010-2014 Thomas Fuchs +// Zepto.js may be freely distributed under the MIT license. + +;(function($){ + var jsonpID = 0, + document = window.document, + key, + name, + rscript = /)<[^<]*)*<\/script>/gi, + scriptTypeRE = /^(?:text|application)\/javascript/i, + xmlTypeRE = /^(?:text|application)\/xml/i, + jsonType = 'application/json', + htmlType = 'text/html', + blankRE = /^\s*$/ + + // trigger a custom event and return false if it was cancelled + function triggerAndReturn(context, eventName, data) { + var event = $.Event(eventName) + $(context).trigger(event, data) + return !event.isDefaultPrevented() + } + + // trigger an Ajax "global" event + function triggerGlobal(settings, context, eventName, data) { + if (settings.global) return triggerAndReturn(context || document, eventName, data) + } + + // Number of active Ajax requests + $.active = 0 + + function ajaxStart(settings) { + if (settings.global && $.active++ === 0) triggerGlobal(settings, null, 'ajaxStart') + } + function ajaxStop(settings) { + if (settings.global && !(--$.active)) triggerGlobal(settings, null, 'ajaxStop') + } + + // triggers an extra global event "ajaxBeforeSend" that's like "ajaxSend" but cancelable + function ajaxBeforeSend(xhr, settings) { + var context = settings.context + if (settings.beforeSend.call(context, xhr, settings) === false || + triggerGlobal(settings, context, 'ajaxBeforeSend', [xhr, settings]) === false) + return false + + triggerGlobal(settings, context, 'ajaxSend', [xhr, settings]) + } + function ajaxSuccess(data, xhr, settings, deferred) { + var context = settings.context, status = 'success' + settings.success.call(context, data, status, xhr) + if (deferred) deferred.resolveWith(context, [data, status, xhr]) + triggerGlobal(settings, context, 'ajaxSuccess', [xhr, settings, data]) + ajaxComplete(status, xhr, settings) + } + // type: "timeout", "error", "abort", "parsererror" + function ajaxError(error, type, xhr, settings, deferred) { + var context = settings.context + settings.error.call(context, xhr, type, error) + if (deferred) deferred.rejectWith(context, [xhr, type, error]) + triggerGlobal(settings, context, 'ajaxError', [xhr, settings, error || type]) + ajaxComplete(type, xhr, settings) + } + // status: "success", "notmodified", "error", "timeout", "abort", "parsererror" + function ajaxComplete(status, xhr, settings) { + var context = settings.context + settings.complete.call(context, xhr, status) + triggerGlobal(settings, context, 'ajaxComplete', [xhr, settings]) + ajaxStop(settings) + } + + // Empty function, used as default callback + function empty() {} + + $.ajaxJSONP = function(options, deferred){ + if (!('type' in options)) return $.ajax(options) + + var _callbackName = options.jsonpCallback, + callbackName = ($.isFunction(_callbackName) ? + _callbackName() : _callbackName) || ('jsonp' + (++jsonpID)), + script = document.createElement('script'), + originalCallback = window[callbackName], + responseData, + abort = function(errorType) { + $(script).triggerHandler('error', errorType || 'abort') + }, + xhr = { abort: abort }, abortTimeout + + if (deferred) deferred.promise(xhr) + + $(script).on('load error', function(e, errorType){ + clearTimeout(abortTimeout) + $(script).off().remove() + + if (e.type == 'error' || !responseData) { + ajaxError(null, errorType || 'error', xhr, options, deferred) + } else { + ajaxSuccess(responseData[0], xhr, options, deferred) + } + + window[callbackName] = originalCallback + if (responseData && $.isFunction(originalCallback)) + originalCallback(responseData[0]) + + originalCallback = responseData = undefined + }) + + if (ajaxBeforeSend(xhr, options) === false) { + abort('abort') + return xhr + } + + window[callbackName] = function(){ + responseData = arguments + } + + script.src = options.url.replace(/\?(.+)=\?/, '?$1=' + callbackName) + document.head.appendChild(script) + + if (options.timeout > 0) abortTimeout = setTimeout(function(){ + abort('timeout') + }, options.timeout) + + return xhr + } + + $.ajaxSettings = { + // Default type of request + type: 'GET', + // Callback that is executed before request + beforeSend: empty, + // Callback that is executed if the request succeeds + success: empty, + // Callback that is executed the the server drops error + error: empty, + // Callback that is executed on request complete (both: error and success) + complete: empty, + // The context for the callbacks + context: null, + // Whether to trigger "global" Ajax events + global: true, + // Transport + xhr: function () { + return new window.XMLHttpRequest() + }, + // MIME types mapping + // IIS returns Javascript as "application/x-javascript" + accepts: { + script: 'text/javascript, application/javascript, application/x-javascript', + json: jsonType, + xml: 'application/xml, text/xml', + html: htmlType, + text: 'text/plain' + }, + // Whether the request is to another domain + crossDomain: false, + // Default timeout + timeout: 0, + // Whether data should be serialized to string + processData: true, + // Whether the browser should be allowed to cache GET responses + cache: true + } + + function mimeToDataType(mime) { + if (mime) mime = mime.split(';', 2)[0] + return mime && ( mime == htmlType ? 'html' : + mime == jsonType ? 'json' : + scriptTypeRE.test(mime) ? 'script' : + xmlTypeRE.test(mime) && 'xml' ) || 'text' + } + + function appendQuery(url, query) { + if (query == '') return url + return (url + '&' + query).replace(/[&?]{1,2}/, '?') + } + + // serialize payload and append it to the URL for GET requests + function serializeData(options) { + if (options.processData && options.data && $.type(options.data) != "string") + options.data = $.param(options.data, options.traditional) + if (options.data && (!options.type || options.type.toUpperCase() == 'GET')) + options.url = appendQuery(options.url, options.data), options.data = undefined + } + + $.ajax = function(options){ + var settings = $.extend({}, options || {}), + deferred = $.Deferred && $.Deferred() + for (key in $.ajaxSettings) if (settings[key] === undefined) settings[key] = $.ajaxSettings[key] + + ajaxStart(settings) + + if (!settings.crossDomain) settings.crossDomain = /^([\w-]+:)?\/\/([^\/]+)/.test(settings.url) && + RegExp.$2 != window.location.host + + if (!settings.url) settings.url = window.location.toString() + serializeData(settings) + if (settings.cache === false) settings.url = appendQuery(settings.url, '_=' + Date.now()) + + var dataType = settings.dataType, hasPlaceholder = /\?.+=\?/.test(settings.url) + if (dataType == 'jsonp' || hasPlaceholder) { + if (!hasPlaceholder) + settings.url = appendQuery(settings.url, + settings.jsonp ? (settings.jsonp + '=?') : settings.jsonp === false ? '' : 'callback=?') + return $.ajaxJSONP(settings, deferred) + } + + var mime = settings.accepts[dataType], + headers = { }, + setHeader = function(name, value) { headers[name.toLowerCase()] = [name, value] }, + protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol, + xhr = settings.xhr(), + nativeSetHeader = xhr.setRequestHeader, + abortTimeout + + if (deferred) deferred.promise(xhr) + + if (!settings.crossDomain) setHeader('X-Requested-With', 'XMLHttpRequest') + setHeader('Accept', mime || '*/*') + if (mime = settings.mimeType || mime) { + if (mime.indexOf(',') > -1) mime = mime.split(',', 2)[0] + xhr.overrideMimeType && xhr.overrideMimeType(mime) + } + if (settings.contentType || (settings.contentType !== false && settings.data && settings.type.toUpperCase() != 'GET')) + setHeader('Content-Type', settings.contentType || 'application/x-www-form-urlencoded') + + if (settings.headers) for (name in settings.headers) setHeader(name, settings.headers[name]) + xhr.setRequestHeader = setHeader + + xhr.onreadystatechange = function(){ + if (xhr.readyState == 4) { + xhr.onreadystatechange = empty + clearTimeout(abortTimeout) + var result, error = false + if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || (xhr.status == 0 && protocol == 'file:')) { + dataType = dataType || mimeToDataType(settings.mimeType || xhr.getResponseHeader('content-type')) + result = xhr.responseText + + try { + // http://perfectionkills.com/global-eval-what-are-the-options/ + if (dataType == 'script') (1,eval)(result) + else if (dataType == 'xml') result = xhr.responseXML + else if (dataType == 'json') result = blankRE.test(result) ? null : $.parseJSON(result) + } catch (e) { error = e } + + if (error) ajaxError(error, 'parsererror', xhr, settings, deferred) + else ajaxSuccess(result, xhr, settings, deferred) + } else { + ajaxError(xhr.statusText || null, xhr.status ? 'error' : 'abort', xhr, settings, deferred) + } + } + } + + if (ajaxBeforeSend(xhr, settings) === false) { + xhr.abort() + ajaxError(null, 'abort', xhr, settings, deferred) + return xhr + } + + if (settings.xhrFields) for (name in settings.xhrFields) xhr[name] = settings.xhrFields[name] + + var async = 'async' in settings ? settings.async : true + xhr.open(settings.type, settings.url, async, settings.username, settings.password) + + for (name in headers) nativeSetHeader.apply(xhr, headers[name]) + + if (settings.timeout > 0) abortTimeout = setTimeout(function(){ + xhr.onreadystatechange = empty + xhr.abort() + ajaxError(null, 'timeout', xhr, settings, deferred) + }, settings.timeout) + + // avoid sending empty string (#319) + xhr.send(settings.data ? settings.data : null) + return xhr + } + + // handle optional data/success arguments + function parseArguments(url, data, success, dataType) { + if ($.isFunction(data)) dataType = success, success = data, data = undefined + if (!$.isFunction(success)) dataType = success, success = undefined + return { + url: url + , data: data + , success: success + , dataType: dataType + } + } + + $.get = function(/* url, data, success, dataType */){ + return $.ajax(parseArguments.apply(null, arguments)) + } + + $.post = function(/* url, data, success, dataType */){ + var options = parseArguments.apply(null, arguments) + options.type = 'POST' + return $.ajax(options) + } + + $.getJSON = function(/* url, data, success */){ + var options = parseArguments.apply(null, arguments) + options.dataType = 'json' + return $.ajax(options) + } + + $.fn.load = function(url, data, success){ + if (!this.length) return this + var self = this, parts = url.split(/\s/), selector, + options = parseArguments(url, data, success), + callback = options.success + if (parts.length > 1) options.url = parts[0], selector = parts[1] + options.success = function(response){ + self.html(selector ? + $('
').html(response.replace(rscript, "")).find(selector) + : response) + callback && callback.apply(self, arguments) + } + $.ajax(options) + return this + } + + var escape = encodeURIComponent + + function serialize(params, obj, traditional, scope){ + var type, array = $.isArray(obj), hash = $.isPlainObject(obj) + $.each(obj, function(key, value) { + type = $.type(value) + if (scope) key = traditional ? scope : + scope + '[' + (hash || type == 'object' || type == 'array' ? key : '') + ']' + // handle data in serializeArray() format + if (!scope && array) params.add(value.name, value.value) + // recurse into nested objects + else if (type == "array" || (!traditional && type == "object")) + serialize(params, value, traditional, key) + else params.add(key, value) + }) + } + + $.param = function(obj, traditional){ + var params = [] + params.add = function(k, v){ this.push(escape(k) + '=' + escape(v)) } + serialize(params, obj, traditional) + return params.join('&').replace(/%20/g, '+') + } +})(Zepto) diff --git a/lib/zeptojs/src/assets.js b/lib/zeptojs/src/assets.js new file mode 100644 index 0000000..0cfdb4e --- /dev/null +++ b/lib/zeptojs/src/assets.js @@ -0,0 +1,21 @@ +// Zepto.js +// (c) 2010-2014 Thomas Fuchs +// Zepto.js may be freely distributed under the MIT license. + +;(function($){ + var cache = [], timeout + + $.fn.remove = function(){ + return this.each(function(){ + if(this.parentNode){ + if(this.tagName === 'IMG'){ + cache.push(this) + this.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=' + if (timeout) clearTimeout(timeout) + timeout = setTimeout(function(){ cache = [] }, 60000) + } + this.parentNode.removeChild(this) + } + }) + } +})(Zepto) diff --git a/lib/zeptojs/src/callbacks.js b/lib/zeptojs/src/callbacks.js new file mode 100644 index 0000000..79cddf8 --- /dev/null +++ b/lib/zeptojs/src/callbacks.js @@ -0,0 +1,122 @@ +// Zepto.js +// (c) 2010-2014 Thomas Fuchs +// Zepto.js may be freely distributed under the MIT license. + +;(function($){ + // Create a collection of callbacks to be fired in a sequence, with configurable behaviour + // Option flags: + // - once: Callbacks fired at most one time. + // - memory: Remember the most recent context and arguments + // - stopOnFalse: Cease iterating over callback list + // - unique: Permit adding at most one instance of the same callback + $.Callbacks = function(options) { + options = $.extend({}, options) + + var memory, // Last fire value (for non-forgettable lists) + fired, // Flag to know if list was already fired + firing, // Flag to know if list is currently firing + firingStart, // First callback to fire (used internally by add and fireWith) + firingLength, // End of the loop when firing + firingIndex, // Index of currently firing callback (modified by remove if needed) + list = [], // Actual callback list + stack = !options.once && [], // Stack of fire calls for repeatable lists + fire = function(data) { + memory = options.memory && data + fired = true + firingIndex = firingStart || 0 + firingStart = 0 + firingLength = list.length + firing = true + for ( ; list && firingIndex < firingLength ; ++firingIndex ) { + if (list[firingIndex].apply(data[0], data[1]) === false && options.stopOnFalse) { + memory = false + break + } + } + firing = false + if (list) { + if (stack) stack.length && fire(stack.shift()) + else if (memory) list.length = 0 + else Callbacks.disable() + } + }, + + Callbacks = { + add: function() { + if (list) { + var start = list.length, + add = function(args) { + $.each(args, function(_, arg){ + if (typeof arg === "function") { + if (!options.unique || !Callbacks.has(arg)) list.push(arg) + } + else if (arg && arg.length && typeof arg !== 'string') add(arg) + }) + } + add(arguments) + if (firing) firingLength = list.length + else if (memory) { + firingStart = start + fire(memory) + } + } + return this + }, + remove: function() { + if (list) { + $.each(arguments, function(_, arg){ + var index + while ((index = $.inArray(arg, list, index)) > -1) { + list.splice(index, 1) + // Handle firing indexes + if (firing) { + if (index <= firingLength) --firingLength + if (index <= firingIndex) --firingIndex + } + } + }) + } + return this + }, + has: function(fn) { + return !!(list && (fn ? $.inArray(fn, list) > -1 : list.length)) + }, + empty: function() { + firingLength = list.length = 0 + return this + }, + disable: function() { + list = stack = memory = undefined + return this + }, + disabled: function() { + return !list + }, + lock: function() { + stack = undefined; + if (!memory) Callbacks.disable() + return this + }, + locked: function() { + return !stack + }, + fireWith: function(context, args) { + if (list && (!fired || stack)) { + args = args || [] + args = [context, args.slice ? args.slice() : args] + if (firing) stack.push(args) + else fire(args) + } + return this + }, + fire: function() { + return Callbacks.fireWith(this, arguments) + }, + fired: function() { + return !!fired + } + } + + return Callbacks + } +})(Zepto) diff --git a/lib/zeptojs/src/data.js b/lib/zeptojs/src/data.js new file mode 100644 index 0000000..b2049da --- /dev/null +++ b/lib/zeptojs/src/data.js @@ -0,0 +1,80 @@ +// Zepto.js +// (c) 2010-2014 Thomas Fuchs +// Zepto.js may be freely distributed under the MIT license. + +// The following code is heavily inspired by jQuery's $.fn.data() + +;(function($){ + var data = {}, dataAttr = $.fn.data, camelize = $.camelCase, + exp = $.expando = 'Zepto' + (+new Date()), emptyArray = [] + + // Get value from node: + // 1. first try key as given, + // 2. then try camelized key, + // 3. fall back to reading "data-*" attribute. + function getData(node, name) { + var id = node[exp], store = id && data[id] + if (name === undefined) return store || setData(node) + else { + if (store) { + if (name in store) return store[name] + var camelName = camelize(name) + if (camelName in store) return store[camelName] + } + return dataAttr.call($(node), name) + } + } + + // Store value under camelized key on node + function setData(node, name, value) { + var id = node[exp] || (node[exp] = ++$.uuid), + store = data[id] || (data[id] = attributeData(node)) + if (name !== undefined) store[camelize(name)] = value + return store + } + + // Read all "data-*" attributes from a node + function attributeData(node) { + var store = {} + $.each(node.attributes || emptyArray, function(i, attr){ + if (attr.name.indexOf('data-') == 0) + store[camelize(attr.name.replace('data-', ''))] = + $.zepto.deserializeValue(attr.value) + }) + return store + } + + $.fn.data = function(name, value) { + return value === undefined ? + // set multiple values via object + $.isPlainObject(name) ? + this.each(function(i, node){ + $.each(name, function(key, value){ setData(node, key, value) }) + }) : + // get value from first element + this.length == 0 ? undefined : getData(this[0], name) : + // set value on all elements + this.each(function(){ setData(this, name, value) }) + } + + $.fn.removeData = function(names) { + if (typeof names == 'string') names = names.split(/\s+/) + return this.each(function(){ + var id = this[exp], store = id && data[id] + if (store) $.each(names || store, function(key){ + delete store[names ? camelize(this) : key] + }) + }) + } + + // Generate extended `remove` and `empty` functions + ;['remove', 'empty'].forEach(function(methodName){ + var origFn = $.fn[methodName] + $.fn[methodName] = function() { + var elements = this.find('*') + if (methodName === 'remove') elements = elements.add(this) + elements.removeData() + return origFn.call(this) + } + }) +})(Zepto) diff --git a/lib/zeptojs/src/deferred.js b/lib/zeptojs/src/deferred.js new file mode 100644 index 0000000..a8e6311 --- /dev/null +++ b/lib/zeptojs/src/deferred.js @@ -0,0 +1,118 @@ +// Zepto.js +// (c) 2010-2014 Thomas Fuchs +// Zepto.js may be freely distributed under the MIT license. +// +// Some code (c) 2005, 2013 jQuery Foundation, Inc. and other contributors + +;(function($){ + var slice = Array.prototype.slice + + function Deferred(func) { + var tuples = [ + // action, add listener, listener list, final state + [ "resolve", "done", $.Callbacks({once:1, memory:1}), "resolved" ], + [ "reject", "fail", $.Callbacks({once:1, memory:1}), "rejected" ], + [ "notify", "progress", $.Callbacks({memory:1}) ] + ], + state = "pending", + promise = { + state: function() { + return state + }, + always: function() { + deferred.done(arguments).fail(arguments) + return this + }, + then: function(/* fnDone [, fnFailed [, fnProgress]] */) { + var fns = arguments + return Deferred(function(defer){ + $.each(tuples, function(i, tuple){ + var fn = $.isFunction(fns[i]) && fns[i] + deferred[tuple[1]](function(){ + var returned = fn && fn.apply(this, arguments) + if (returned && $.isFunction(returned.promise)) { + returned.promise() + .done(defer.resolve) + .fail(defer.reject) + .progress(defer.notify) + } else { + var context = this === promise ? defer.promise() : this, + values = fn ? [returned] : arguments + defer[tuple[0] + "With"](context, values) + } + }) + }) + fns = null + }).promise() + }, + + promise: function(obj) { + return obj != null ? $.extend( obj, promise ) : promise + } + }, + deferred = {} + + $.each(tuples, function(i, tuple){ + var list = tuple[2], + stateString = tuple[3] + + promise[tuple[1]] = list.add + + if (stateString) { + list.add(function(){ + state = stateString + }, tuples[i^1][2].disable, tuples[2][2].lock) + } + + deferred[tuple[0]] = function(){ + deferred[tuple[0] + "With"](this === deferred ? promise : this, arguments) + return this + } + deferred[tuple[0] + "With"] = list.fireWith + }) + + promise.promise(deferred) + if (func) func.call(deferred, deferred) + return deferred + } + + $.when = function(sub) { + var resolveValues = slice.call(arguments), + len = resolveValues.length, + i = 0, + remain = len !== 1 || (sub && $.isFunction(sub.promise)) ? len : 0, + deferred = remain === 1 ? sub : Deferred(), + progressValues, progressContexts, resolveContexts, + updateFn = function(i, ctx, val){ + return function(value){ + ctx[i] = this + val[i] = arguments.length > 1 ? slice.call(arguments) : value + if (val === progressValues) { + deferred.notifyWith(ctx, val) + } else if (!(--remain)) { + deferred.resolveWith(ctx, val) + } + } + } + + if (len > 1) { + progressValues = new Array(len) + progressContexts = new Array(len) + resolveContexts = new Array(len) + for ( ; i < len; ++i ) { + if (resolveValues[i] && $.isFunction(resolveValues[i].promise)) { + resolveValues[i].promise() + .done(updateFn(i, resolveContexts, resolveValues)) + .fail(deferred.reject) + .progress(updateFn(i, progressContexts, progressValues)) + } else { + --remain + } + } + } + if (!remain) deferred.resolveWith(resolveContexts, resolveValues) + return deferred.promise() + } + + $.Deferred = Deferred +})(Zepto) diff --git a/lib/zeptojs/src/detect.js b/lib/zeptojs/src/detect.js new file mode 100644 index 0000000..dc8b2a3 --- /dev/null +++ b/lib/zeptojs/src/detect.js @@ -0,0 +1,65 @@ +// Zepto.js +// (c) 2010-2014 Thomas Fuchs +// Zepto.js may be freely distributed under the MIT license. + +;(function($){ + function detect(ua){ + var os = this.os = {}, browser = this.browser = {}, + webkit = ua.match(/Web[kK]it[\/]{0,1}([\d.]+)/), + android = ua.match(/(Android);?[\s\/]+([\d.]+)?/), + ipad = ua.match(/(iPad).*OS\s([\d_]+)/), + ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/), + iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/), + webos = ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/), + touchpad = webos && ua.match(/TouchPad/), + kindle = ua.match(/Kindle\/([\d.]+)/), + silk = ua.match(/Silk\/([\d._]+)/), + blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/), + bb10 = ua.match(/(BB10).*Version\/([\d.]+)/), + rimtabletos = ua.match(/(RIM\sTablet\sOS)\s([\d.]+)/), + playbook = ua.match(/PlayBook/), + chrome = ua.match(/Chrome\/([\d.]+)/) || ua.match(/CriOS\/([\d.]+)/), + firefox = ua.match(/Firefox\/([\d.]+)/), + ie = ua.match(/MSIE\s([\d.]+)/), + safari = webkit && ua.match(/Mobile\//) && !chrome, + webview = ua.match(/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/) && !chrome + + // Todo: clean this up with a better OS/browser seperation: + // - discern (more) between multiple browsers on android + // - decide if kindle fire in silk mode is android or not + // - Firefox on Android doesn't specify the Android version + // - possibly devide in os, device and browser hashes + + if (browser.webkit = !!webkit) browser.version = webkit[1] + + if (android) os.android = true, os.version = android[2] + if (iphone && !ipod) os.ios = os.iphone = true, os.version = iphone[2].replace(/_/g, '.') + if (ipad) os.ios = os.ipad = true, os.version = ipad[2].replace(/_/g, '.') + if (ipod) os.ios = os.ipod = true, os.version = ipod[3] ? ipod[3].replace(/_/g, '.') : null + if (webos) os.webos = true, os.version = webos[2] + if (touchpad) os.touchpad = true + if (blackberry) os.blackberry = true, os.version = blackberry[2] + if (bb10) os.bb10 = true, os.version = bb10[2] + if (rimtabletos) os.rimtabletos = true, os.version = rimtabletos[2] + if (playbook) browser.playbook = true + if (kindle) os.kindle = true, os.version = kindle[1] + if (silk) browser.silk = true, browser.version = silk[1] + if (!silk && os.android && ua.match(/Kindle Fire/)) browser.silk = true + if (chrome) browser.chrome = true, browser.version = chrome[1] + if (firefox) browser.firefox = true, browser.version = firefox[1] + if (ie) browser.ie = true, browser.version = ie[1] + if (safari && (ua.match(/Safari/) || !!os.ios)) browser.safari = true + if (webview) browser.webview = true + + os.tablet = !!(ipad || playbook || (android && !ua.match(/Mobile/)) || + (firefox && ua.match(/Tablet/)) || (ie && !ua.match(/Phone/) && ua.match(/Touch/))) + os.phone = !!(!os.tablet && !os.ipod && (android || iphone || webos || blackberry || bb10 || + (chrome && ua.match(/Android/)) || (chrome && ua.match(/CriOS\/([\d.]+)/)) || + (firefox && ua.match(/Mobile/)) || (ie && ua.match(/Touch/)))) + } + + detect.call($, navigator.userAgent) + // make available to unit tests + $.__detect = detect + +})(Zepto) diff --git a/lib/zeptojs/src/event.js b/lib/zeptojs/src/event.js new file mode 100644 index 0000000..7ab2af4 --- /dev/null +++ b/lib/zeptojs/src/event.js @@ -0,0 +1,276 @@ +// Zepto.js +// (c) 2010-2014 Thomas Fuchs +// Zepto.js may be freely distributed under the MIT license. + +;(function($){ + var _zid = 1, undefined, + slice = Array.prototype.slice, + isFunction = $.isFunction, + isString = function(obj){ return typeof obj == 'string' }, + handlers = {}, + specialEvents={}, + focusinSupported = 'onfocusin' in window, + focus = { focus: 'focusin', blur: 'focusout' }, + hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' } + + specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents' + + function zid(element) { + return element._zid || (element._zid = _zid++) + } + function findHandlers(element, event, fn, selector) { + event = parse(event) + if (event.ns) var matcher = matcherFor(event.ns) + return (handlers[zid(element)] || []).filter(function(handler) { + return handler + && (!event.e || handler.e == event.e) + && (!event.ns || matcher.test(handler.ns)) + && (!fn || zid(handler.fn) === zid(fn)) + && (!selector || handler.sel == selector) + }) + } + function parse(event) { + var parts = ('' + event).split('.') + return {e: parts[0], ns: parts.slice(1).sort().join(' ')} + } + function matcherFor(ns) { + return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)') + } + + function eventCapture(handler, captureSetting) { + return handler.del && + (!focusinSupported && (handler.e in focus)) || + !!captureSetting + } + + function realEvent(type) { + return hover[type] || (focusinSupported && focus[type]) || type + } + + function add(element, events, fn, data, selector, delegator, capture){ + var id = zid(element), set = (handlers[id] || (handlers[id] = [])) + events.split(/\s/).forEach(function(event){ + if (event == 'ready') return $(document).ready(fn) + var handler = parse(event) + handler.fn = fn + handler.sel = selector + // emulate mouseenter, mouseleave + if (handler.e in hover) fn = function(e){ + var related = e.relatedTarget + if (!related || (related !== this && !$.contains(this, related))) + return handler.fn.apply(this, arguments) + } + handler.del = delegator + var callback = delegator || fn + handler.proxy = function(e){ + e = compatible(e) + if (e.isImmediatePropagationStopped()) return + e.data = data + var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args)) + if (result === false) e.preventDefault(), e.stopPropagation() + return result + } + handler.i = set.length + set.push(handler) + if ('addEventListener' in element) + element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) + }) + } + function remove(element, events, fn, selector, capture){ + var id = zid(element) + ;(events || '').split(/\s/).forEach(function(event){ + findHandlers(element, event, fn, selector).forEach(function(handler){ + delete handlers[id][handler.i] + if ('removeEventListener' in element) + element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) + }) + }) + } + + $.event = { add: add, remove: remove } + + $.proxy = function(fn, context) { + if (isFunction(fn)) { + var proxyFn = function(){ return fn.apply(context, arguments) } + proxyFn._zid = zid(fn) + return proxyFn + } else if (isString(context)) { + return $.proxy(fn[context], fn) + } else { + throw new TypeError("expected function") + } + } + + $.fn.bind = function(event, data, callback){ + return this.on(event, data, callback) + } + $.fn.unbind = function(event, callback){ + return this.off(event, callback) + } + $.fn.one = function(event, selector, data, callback){ + return this.on(event, selector, data, callback, 1) + } + + var returnTrue = function(){return true}, + returnFalse = function(){return false}, + ignoreProperties = /^([A-Z]|returnValue$|layer[XY]$)/, + eventMethods = { + preventDefault: 'isDefaultPrevented', + stopImmediatePropagation: 'isImmediatePropagationStopped', + stopPropagation: 'isPropagationStopped' + } + + function compatible(event, source) { + if (source || !event.isDefaultPrevented) { + source || (source = event) + + $.each(eventMethods, function(name, predicate) { + var sourceMethod = source[name] + event[name] = function(){ + this[predicate] = returnTrue + return sourceMethod && sourceMethod.apply(source, arguments) + } + event[predicate] = returnFalse + }) + + if (source.defaultPrevented !== undefined ? source.defaultPrevented : + 'returnValue' in source ? source.returnValue === false : + source.getPreventDefault && source.getPreventDefault()) + event.isDefaultPrevented = returnTrue + } + return event + } + + function createProxy(event) { + var key, proxy = { originalEvent: event } + for (key in event) + if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key] + + return compatible(proxy, event) + } + + $.fn.delegate = function(selector, event, callback){ + return this.on(event, selector, callback) + } + $.fn.undelegate = function(selector, event, callback){ + return this.off(event, selector, callback) + } + + $.fn.live = function(event, callback){ + $(document.body).delegate(this.selector, event, callback) + return this + } + $.fn.die = function(event, callback){ + $(document.body).undelegate(this.selector, event, callback) + return this + } + + $.fn.on = function(event, selector, data, callback, one){ + var autoRemove, delegator, $this = this + if (event && !isString(event)) { + $.each(event, function(type, fn){ + $this.on(type, selector, data, fn, one) + }) + return $this + } + + if (!isString(selector) && !isFunction(callback) && callback !== false) + callback = data, data = selector, selector = undefined + if (isFunction(data) || data === false) + callback = data, data = undefined + + if (callback === false) callback = returnFalse + + return $this.each(function(_, element){ + if (one) autoRemove = function(e){ + remove(element, e.type, callback) + return callback.apply(this, arguments) + } + + if (selector) delegator = function(e){ + var evt, match = $(e.target).closest(selector, element).get(0) + if (match && match !== element) { + evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element}) + return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1))) + } + } + + add(element, event, callback, data, selector, delegator || autoRemove) + }) + } + $.fn.off = function(event, selector, callback){ + var $this = this + if (event && !isString(event)) { + $.each(event, function(type, fn){ + $this.off(type, selector, fn) + }) + return $this + } + + if (!isString(selector) && !isFunction(callback) && callback !== false) + callback = selector, selector = undefined + + if (callback === false) callback = returnFalse + + return $this.each(function(){ + remove(this, event, callback, selector) + }) + } + + $.fn.trigger = function(event, args){ + event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event) + event._args = args + return this.each(function(){ + // items in the collection might not be DOM elements + if('dispatchEvent' in this) this.dispatchEvent(event) + else $(this).triggerHandler(event, args) + }) + } + + // triggers event handlers on current element just as if an event occurred, + // doesn't trigger an actual event, doesn't bubble + $.fn.triggerHandler = function(event, args){ + var e, result + this.each(function(i, element){ + e = createProxy(isString(event) ? $.Event(event) : event) + e._args = args + e.target = element + $.each(findHandlers(element, event.type || event), function(i, handler){ + result = handler.proxy(e) + if (e.isImmediatePropagationStopped()) return false + }) + }) + return result + } + + // shortcut methods for `.bind(event, fn)` for each event type + ;('focusin focusout load resize scroll unload click dblclick '+ + 'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+ + 'change select keydown keypress keyup error').split(' ').forEach(function(event) { + $.fn[event] = function(callback) { + return callback ? + this.bind(event, callback) : + this.trigger(event) + } + }) + + ;['focus', 'blur'].forEach(function(name) { + $.fn[name] = function(callback) { + if (callback) this.bind(name, callback) + else this.each(function(){ + try { this[name]() } + catch(e) {} + }) + return this + } + }) + + $.Event = function(type, props) { + if (!isString(type)) props = type, type = props.type + var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true + if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name]) + event.initEvent(type, bubbles, true) + return compatible(event) + } + +})(Zepto) diff --git a/lib/zeptojs/src/form.js b/lib/zeptojs/src/form.js new file mode 100644 index 0000000..17bde52 --- /dev/null +++ b/lib/zeptojs/src/form.js @@ -0,0 +1,40 @@ +// Zepto.js +// (c) 2010-2014 Thomas Fuchs +// Zepto.js may be freely distributed under the MIT license. + +;(function($){ + $.fn.serializeArray = function() { + var result = [], el + $([].slice.call(this.get(0).elements)).each(function(){ + el = $(this) + var type = el.attr('type') + if (this.nodeName.toLowerCase() != 'fieldset' && + !this.disabled && type != 'submit' && type != 'reset' && type != 'button' && + ((type != 'radio' && type != 'checkbox') || this.checked)) + result.push({ + name: el.attr('name'), + value: el.val() + }) + }) + return result + } + + $.fn.serialize = function(){ + var result = [] + this.serializeArray().forEach(function(elm){ + result.push(encodeURIComponent(elm.name) + '=' + encodeURIComponent(elm.value)) + }) + return result.join('&') + } + + $.fn.submit = function(callback) { + if (callback) this.bind('submit', callback) + else if (this.length) { + var event = $.Event('submit') + this.eq(0).trigger(event) + if (!event.isDefaultPrevented()) this.get(0).submit() + } + return this + } + +})(Zepto) diff --git a/lib/zeptojs/src/fx.js b/lib/zeptojs/src/fx.js new file mode 100644 index 0000000..c36c577 --- /dev/null +++ b/lib/zeptojs/src/fx.js @@ -0,0 +1,123 @@ +// Zepto.js +// (c) 2010-2014 Thomas Fuchs +// Zepto.js may be freely distributed under the MIT license. + +;(function($, undefined){ + var prefix = '', eventPrefix, endEventName, endAnimationName, + vendors = { Webkit: 'webkit', Moz: '', O: 'o' }, + document = window.document, testEl = document.createElement('div'), + supportedTransforms = /^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i, + transform, + transitionProperty, transitionDuration, transitionTiming, transitionDelay, + animationName, animationDuration, animationTiming, animationDelay, + cssReset = {} + + function dasherize(str) { return str.replace(/([a-z])([A-Z])/, '$1-$2').toLowerCase() } + function normalizeEvent(name) { return eventPrefix ? eventPrefix + name : name.toLowerCase() } + + $.each(vendors, function(vendor, event){ + if (testEl.style[vendor + 'TransitionProperty'] !== undefined) { + prefix = '-' + vendor.toLowerCase() + '-' + eventPrefix = event + return false + } + }) + + transform = prefix + 'transform' + cssReset[transitionProperty = prefix + 'transition-property'] = + cssReset[transitionDuration = prefix + 'transition-duration'] = + cssReset[transitionDelay = prefix + 'transition-delay'] = + cssReset[transitionTiming = prefix + 'transition-timing-function'] = + cssReset[animationName = prefix + 'animation-name'] = + cssReset[animationDuration = prefix + 'animation-duration'] = + cssReset[animationDelay = prefix + 'animation-delay'] = + cssReset[animationTiming = prefix + 'animation-timing-function'] = '' + + $.fx = { + off: (eventPrefix === undefined && testEl.style.transitionProperty === undefined), + speeds: { _default: 400, fast: 200, slow: 600 }, + cssPrefix: prefix, + transitionEnd: normalizeEvent('TransitionEnd'), + animationEnd: normalizeEvent('AnimationEnd') + } + + $.fn.animate = function(properties, duration, ease, callback, delay){ + if ($.isFunction(duration)) + callback = duration, ease = undefined, duration = undefined + if ($.isFunction(ease)) + callback = ease, ease = undefined + if ($.isPlainObject(duration)) + ease = duration.easing, callback = duration.complete, delay = duration.delay, duration = duration.duration + if (duration) duration = (typeof duration == 'number' ? duration : + ($.fx.speeds[duration] || $.fx.speeds._default)) / 1000 + if (delay) delay = parseFloat(delay) / 1000 + return this.anim(properties, duration, ease, callback, delay) + } + + $.fn.anim = function(properties, duration, ease, callback, delay){ + var key, cssValues = {}, cssProperties, transforms = '', + that = this, wrappedCallback, endEvent = $.fx.transitionEnd, + fired = false + + if (duration === undefined) duration = $.fx.speeds._default / 1000 + if (delay === undefined) delay = 0 + if ($.fx.off) duration = 0 + + if (typeof properties == 'string') { + // keyframe animation + cssValues[animationName] = properties + cssValues[animationDuration] = duration + 's' + cssValues[animationDelay] = delay + 's' + cssValues[animationTiming] = (ease || 'linear') + endEvent = $.fx.animationEnd + } else { + cssProperties = [] + // CSS transitions + for (key in properties) + if (supportedTransforms.test(key)) transforms += key + '(' + properties[key] + ') ' + else cssValues[key] = properties[key], cssProperties.push(dasherize(key)) + + if (transforms) cssValues[transform] = transforms, cssProperties.push(transform) + if (duration > 0 && typeof properties === 'object') { + cssValues[transitionProperty] = cssProperties.join(', ') + cssValues[transitionDuration] = duration + 's' + cssValues[transitionDelay] = delay + 's' + cssValues[transitionTiming] = (ease || 'linear') + } + } + + wrappedCallback = function(event){ + if (typeof event !== 'undefined') { + if (event.target !== event.currentTarget) return // makes sure the event didn't bubble from "below" + $(event.target).unbind(endEvent, wrappedCallback) + } else + $(this).unbind(endEvent, wrappedCallback) // triggered by setTimeout + + fired = true + $(this).css(cssReset) + callback && callback.call(this) + } + if (duration > 0){ + this.bind(endEvent, wrappedCallback) + // transitionEnd is not always firing on older Android phones + // so make sure it gets fired + setTimeout(function(){ + if (fired) return + wrappedCallback.call(that) + }, (duration * 1000) + 25) + } + + // trigger page reflow so new elements can animate + this.size() && this.get(0).clientLeft + + this.css(cssValues) + + if (duration <= 0) setTimeout(function() { + that.each(function(){ wrappedCallback.call(this) }) + }, 0) + + return this + } + + testEl = null +})(Zepto) diff --git a/lib/zeptojs/src/fx_methods.js b/lib/zeptojs/src/fx_methods.js new file mode 100644 index 0000000..f92ac52 --- /dev/null +++ b/lib/zeptojs/src/fx_methods.js @@ -0,0 +1,71 @@ +// Zepto.js +// (c) 2010-2014 Thomas Fuchs +// Zepto.js may be freely distributed under the MIT license. + +;(function($, undefined){ + var document = window.document, docElem = document.documentElement, + origShow = $.fn.show, origHide = $.fn.hide, origToggle = $.fn.toggle + + function anim(el, speed, opacity, scale, callback) { + if (typeof speed == 'function' && !callback) callback = speed, speed = undefined + var props = { opacity: opacity } + if (scale) { + props.scale = scale + el.css($.fx.cssPrefix + 'transform-origin', '0 0') + } + return el.animate(props, speed, null, callback) + } + + function hide(el, speed, scale, callback) { + return anim(el, speed, 0, scale, function(){ + origHide.call($(this)) + callback && callback.call(this) + }) + } + + $.fn.show = function(speed, callback) { + origShow.call(this) + if (speed === undefined) speed = 0 + else this.css('opacity', 0) + return anim(this, speed, 1, '1,1', callback) + } + + $.fn.hide = function(speed, callback) { + if (speed === undefined) return origHide.call(this) + else return hide(this, speed, '0,0', callback) + } + + $.fn.toggle = function(speed, callback) { + if (speed === undefined || typeof speed == 'boolean') + return origToggle.call(this, speed) + else return this.each(function(){ + var el = $(this) + el[el.css('display') == 'none' ? 'show' : 'hide'](speed, callback) + }) + } + + $.fn.fadeTo = function(speed, opacity, callback) { + return anim(this, speed, opacity, null, callback) + } + + $.fn.fadeIn = function(speed, callback) { + var target = this.css('opacity') + if (target > 0) this.css('opacity', 0) + else target = 1 + return origShow.call(this).fadeTo(speed, target, callback) + } + + $.fn.fadeOut = function(speed, callback) { + return hide(this, speed, null, callback) + } + + $.fn.fadeToggle = function(speed, callback) { + return this.each(function(){ + var el = $(this) + el[ + (el.css('opacity') == 0 || el.css('display') == 'none') ? 'fadeIn' : 'fadeOut' + ](speed, callback) + }) + } + +})(Zepto) diff --git a/lib/zeptojs/src/gesture.js b/lib/zeptojs/src/gesture.js new file mode 100644 index 0000000..b9cfd44 --- /dev/null +++ b/lib/zeptojs/src/gesture.js @@ -0,0 +1,35 @@ +// Zepto.js +// (c) 2010-2014 Thomas Fuchs +// Zepto.js may be freely distributed under the MIT license. + +;(function($){ + if ($.os.ios) { + var gesture = {}, gestureTimeout + + function parentIfText(node){ + return 'tagName' in node ? node : node.parentNode + } + + $(document).bind('gesturestart', function(e){ + var now = Date.now(), delta = now - (gesture.last || now) + gesture.target = parentIfText(e.target) + gestureTimeout && clearTimeout(gestureTimeout) + gesture.e1 = e.scale + gesture.last = now + }).bind('gesturechange', function(e){ + gesture.e2 = e.scale + }).bind('gestureend', function(e){ + if (gesture.e2 > 0) { + Math.abs(gesture.e1 - gesture.e2) != 0 && $(gesture.target).trigger('pinch') && + $(gesture.target).trigger('pinch' + (gesture.e1 - gesture.e2 > 0 ? 'In' : 'Out')) + gesture.e1 = gesture.e2 = gesture.last = 0 + } else if ('last' in gesture) { + gesture = {} + } + }) + + ;['pinch', 'pinchIn', 'pinchOut'].forEach(function(m){ + $.fn[m] = function(callback){ return this.bind(m, callback) } + }) + } +})(Zepto) diff --git a/lib/zeptojs/src/ie.js b/lib/zeptojs/src/ie.js new file mode 100644 index 0000000..d3bb1fd --- /dev/null +++ b/lib/zeptojs/src/ie.js @@ -0,0 +1,38 @@ +// Zepto.js +// (c) 2010-2014 Thomas Fuchs +// Zepto.js may be freely distributed under the MIT license. + +;(function($){ + // __proto__ doesn't exist on IE<11, so redefine + // the Z function to use object extension instead + if (!('__proto__' in {})) { + $.extend($.zepto, { + Z: function(dom, selector){ + dom = dom || [] + $.extend(dom, $.fn) + dom.selector = selector || '' + dom.__Z = true + return dom + }, + // this is a kludge but works + isZ: function(object){ + return $.type(object) === 'array' && '__Z' in object + } + }) + } + + // getComputedStyle shouldn't freak out when called + // without a valid element as argument + try { + getComputedStyle(undefined) + } catch(e) { + var nativeGetComputedStyle = getComputedStyle; + window.getComputedStyle = function(element){ + try { + return nativeGetComputedStyle(element) + } catch(e) { + return null + } + } + } +})(Zepto) diff --git a/lib/zeptojs/src/ios3.js b/lib/zeptojs/src/ios3.js new file mode 100644 index 0000000..b5eb7c3 --- /dev/null +++ b/lib/zeptojs/src/ios3.js @@ -0,0 +1,36 @@ +// Zepto.js +// (c) 2010-2014 Thomas Fuchs +// Zepto.js may be freely distributed under the MIT license. + +;(function(undefined){ + if (String.prototype.trim === undefined) // fix for iOS 3.2 + String.prototype.trim = function(){ return this.replace(/^\s+|\s+$/g, '') } + + // For iOS 3.x + // from https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/reduce + if (Array.prototype.reduce === undefined) + Array.prototype.reduce = function(fun){ + if(this === void 0 || this === null) throw new TypeError() + var t = Object(this), len = t.length >>> 0, k = 0, accumulator + if(typeof fun != 'function') throw new TypeError() + if(len == 0 && arguments.length == 1) throw new TypeError() + + if(arguments.length >= 2) + accumulator = arguments[1] + else + do{ + if(k in t){ + accumulator = t[k++] + break + } + if(++k >= len) throw new TypeError() + } while (true) + + while (k < len){ + if(k in t) accumulator = fun.call(undefined, accumulator, t[k], k, t) + k++ + } + return accumulator + } + +})() diff --git a/lib/zeptojs/src/selector.js b/lib/zeptojs/src/selector.js new file mode 100644 index 0000000..c1d5fa8 --- /dev/null +++ b/lib/zeptojs/src/selector.js @@ -0,0 +1,85 @@ +// Zepto.js +// (c) 2010-2014 Thomas Fuchs +// Zepto.js may be freely distributed under the MIT license. + +;(function($){ + var zepto = $.zepto, oldQsa = zepto.qsa, oldMatches = zepto.matches + + function visible(elem){ + elem = $(elem) + return !!(elem.width() || elem.height()) && elem.css("display") !== "none" + } + + // Implements a subset from: + // http://api.jquery.com/category/selectors/jquery-selector-extensions/ + // + // Each filter function receives the current index, all nodes in the + // considered set, and a value if there were parentheses. The value + // of `this` is the node currently being considered. The function returns the + // resulting node(s), null, or undefined. + // + // Complex selectors are not supported: + // li:has(label:contains("foo")) + li:has(label:contains("bar")) + // ul.inner:first > li + var filters = $.expr[':'] = { + visible: function(){ if (visible(this)) return this }, + hidden: function(){ if (!visible(this)) return this }, + selected: function(){ if (this.selected) return this }, + checked: function(){ if (this.checked) return this }, + parent: function(){ return this.parentNode }, + first: function(idx){ if (idx === 0) return this }, + last: function(idx, nodes){ if (idx === nodes.length - 1) return this }, + eq: function(idx, _, value){ if (idx === value) return this }, + contains: function(idx, _, text){ if ($(this).text().indexOf(text) > -1) return this }, + has: function(idx, _, sel){ if (zepto.qsa(this, sel).length) return this } + } + + var filterRe = new RegExp('(.*):(\\w+)(?:\\(([^)]+)\\))?$\\s*'), + childRe = /^\s*>/, + classTag = 'Zepto' + (+new Date()) + + function process(sel, fn) { + // quote the hash in `a[href^=#]` expression + sel = sel.replace(/=#\]/g, '="#"]') + var filter, arg, match = filterRe.exec(sel) + if (match && match[2] in filters) { + filter = filters[match[2]], arg = match[3] + sel = match[1] + if (arg) { + var num = Number(arg) + if (isNaN(num)) arg = arg.replace(/^["']|["']$/g, '') + else arg = num + } + } + return fn(sel, filter, arg) + } + + zepto.qsa = function(node, selector) { + return process(selector, function(sel, filter, arg){ + try { + var taggedParent + if (!sel && filter) sel = '*' + else if (childRe.test(sel)) + // support "> *" child queries by tagging the parent node with a + // unique class and prepending that classname onto the selector + taggedParent = $(node).addClass(classTag), sel = '.'+classTag+' '+sel + + var nodes = oldQsa(node, sel) + } catch(e) { + console.error('error performing selector: %o', selector) + throw e + } finally { + if (taggedParent) taggedParent.removeClass(classTag) + } + return !filter ? nodes : + zepto.uniq($.map(nodes, function(n, i){ return filter.call(n, i, nodes, arg) })) + }) + } + + zepto.matches = function(node, selector){ + return process(selector, function(sel, filter, arg){ + return (!sel || oldMatches(node, sel)) && + (!filter || filter.call(node, null, arg) === node) + }) + } +})(Zepto) diff --git a/lib/zeptojs/src/stack.js b/lib/zeptojs/src/stack.js new file mode 100644 index 0000000..cbec614 --- /dev/null +++ b/lib/zeptojs/src/stack.js @@ -0,0 +1,22 @@ +// Zepto.js +// (c) 2010-2014 Thomas Fuchs +// Zepto.js may be freely distributed under the MIT license. + +;(function($){ + $.fn.end = function(){ + return this.prevObject || $() + } + + $.fn.andSelf = function(){ + return this.add(this.prevObject || $()) + } + + 'filter,add,not,eq,first,last,find,closest,parents,parent,children,siblings'.split(',').forEach(function(property){ + var fn = $.fn[property] + $.fn[property] = function(){ + var ret = fn.apply(this, arguments) + ret.prevObject = this + return ret + } + }) +})(Zepto) diff --git a/lib/zeptojs/src/touch.js b/lib/zeptojs/src/touch.js new file mode 100644 index 0000000..c449212 --- /dev/null +++ b/lib/zeptojs/src/touch.js @@ -0,0 +1,165 @@ +// Zepto.js +// (c) 2010-2014 Thomas Fuchs +// Zepto.js may be freely distributed under the MIT license. + +;(function($){ + var touch = {}, + touchTimeout, tapTimeout, swipeTimeout, longTapTimeout, + longTapDelay = 750, + gesture + + function swipeDirection(x1, x2, y1, y2) { + return Math.abs(x1 - x2) >= + Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down') + } + + function longTap() { + longTapTimeout = null + if (touch.last) { + touch.el.trigger('longTap') + touch = {} + } + } + + function cancelLongTap() { + if (longTapTimeout) clearTimeout(longTapTimeout) + longTapTimeout = null + } + + function cancelAll() { + if (touchTimeout) clearTimeout(touchTimeout) + if (tapTimeout) clearTimeout(tapTimeout) + if (swipeTimeout) clearTimeout(swipeTimeout) + if (longTapTimeout) clearTimeout(longTapTimeout) + touchTimeout = tapTimeout = swipeTimeout = longTapTimeout = null + touch = {} + } + + function isPrimaryTouch(event){ + return (event.pointerType == 'touch' || + event.pointerType == event.MSPOINTER_TYPE_TOUCH) + && event.isPrimary + } + + function isPointerEventType(e, type){ + return (e.type == 'pointer'+type || + e.type.toLowerCase() == 'mspointer'+type) + } + + $(document).ready(function(){ + var now, delta, deltaX = 0, deltaY = 0, firstTouch, _isPointerType + + if ('MSGesture' in window) { + gesture = new MSGesture() + gesture.target = document.body + } + + $(document) + .bind('MSGestureEnd', function(e){ + var swipeDirectionFromVelocity = + e.velocityX > 1 ? 'Right' : e.velocityX < -1 ? 'Left' : e.velocityY > 1 ? 'Down' : e.velocityY < -1 ? 'Up' : null; + if (swipeDirectionFromVelocity) { + touch.el.trigger('swipe') + touch.el.trigger('swipe'+ swipeDirectionFromVelocity) + } + }) + .on('touchstart MSPointerDown pointerdown', function(e){ + if((_isPointerType = isPointerEventType(e, 'down')) && + !isPrimaryTouch(e)) return + firstTouch = _isPointerType ? e : e.touches[0] + if (e.touches && e.touches.length === 1 && touch.x2) { + // Clear out touch movement data if we have it sticking around + // This can occur if touchcancel doesn't fire due to preventDefault, etc. + touch.x2 = undefined + touch.y2 = undefined + } + now = Date.now() + delta = now - (touch.last || now) + touch.el = $('tagName' in firstTouch.target ? + firstTouch.target : firstTouch.target.parentNode) + touchTimeout && clearTimeout(touchTimeout) + touch.x1 = firstTouch.pageX + touch.y1 = firstTouch.pageY + if (delta > 0 && delta <= 250) touch.isDoubleTap = true + touch.last = now + longTapTimeout = setTimeout(longTap, longTapDelay) + // adds the current touch contact for IE gesture recognition + if (gesture && _isPointerType) gesture.addPointer(e.pointerId); + }) + .on('touchmove MSPointerMove pointermove', function(e){ + if((_isPointerType = isPointerEventType(e, 'move')) && + !isPrimaryTouch(e)) return + firstTouch = _isPointerType ? e : e.touches[0] + cancelLongTap() + touch.x2 = firstTouch.pageX + touch.y2 = firstTouch.pageY + + deltaX += Math.abs(touch.x1 - touch.x2) + deltaY += Math.abs(touch.y1 - touch.y2) + }) + .on('touchend MSPointerUp pointerup', function(e){ + if((_isPointerType = isPointerEventType(e, 'up')) && + !isPrimaryTouch(e)) return + cancelLongTap() + + // swipe + if ((touch.x2 && Math.abs(touch.x1 - touch.x2) > 30) || + (touch.y2 && Math.abs(touch.y1 - touch.y2) > 30)) + + swipeTimeout = setTimeout(function() { + touch.el.trigger('swipe') + touch.el.trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2))) + touch = {} + }, 0) + + // normal tap + else if ('last' in touch) + // don't fire tap when delta position changed by more than 30 pixels, + // for instance when moving to a point and back to origin + if (deltaX < 30 && deltaY < 30) { + // delay by one tick so we can cancel the 'tap' event if 'scroll' fires + // ('tap' fires before 'scroll') + tapTimeout = setTimeout(function() { + + // trigger universal 'tap' with the option to cancelTouch() + // (cancelTouch cancels processing of single vs double taps for faster 'tap' response) + var event = $.Event('tap') + event.cancelTouch = cancelAll + touch.el.trigger(event) + + // trigger double tap immediately + if (touch.isDoubleTap) { + if (touch.el) touch.el.trigger('doubleTap') + touch = {} + } + + // trigger single tap after 250ms of inactivity + else { + touchTimeout = setTimeout(function(){ + touchTimeout = null + if (touch.el) touch.el.trigger('singleTap') + touch = {} + }, 250) + } + }, 0) + } else { + touch = {} + } + deltaX = deltaY = 0 + + }) + // when the browser window loses focus, + // for example when a modal dialog is shown, + // cancel all ongoing events + .on('touchcancel MSPointerCancel pointercancel', cancelAll) + + // scrolling the window indicates intention of the user + // to scroll, not tap or swipe, so cancel all ongoing events + $(window).on('scroll', cancelAll) + }) + + ;['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', + 'doubleTap', 'tap', 'singleTap', 'longTap'].forEach(function(eventName){ + $.fn[eventName] = function(callback){ return this.on(eventName, callback) } + }) +})(Zepto) diff --git a/lib/zeptojs/src/zepto.js b/lib/zeptojs/src/zepto.js new file mode 100644 index 0000000..23ba6af --- /dev/null +++ b/lib/zeptojs/src/zepto.js @@ -0,0 +1,866 @@ +// Zepto.js +// (c) 2010-2014 Thomas Fuchs +// Zepto.js may be freely distributed under the MIT license. + +var Zepto = (function() { + var undefined, key, $, classList, emptyArray = [], slice = emptyArray.slice, filter = emptyArray.filter, + document = window.document, + elementDisplay = {}, classCache = {}, + cssNumber = { 'column-count': 1, 'columns': 1, 'font-weight': 1, 'line-height': 1,'opacity': 1, 'z-index': 1, 'zoom': 1 }, + fragmentRE = /^\s*<(\w+|!)[^>]*>/, + singleTagRE = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, + tagExpanderRE = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, + rootNodeRE = /^(?:body|html)$/i, + capitalRE = /([A-Z])/g, + + // special attributes that should be get/set via method calls + methodAttributes = ['val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset'], + + adjacencyOperators = [ 'after', 'prepend', 'before', 'append' ], + table = document.createElement('table'), + tableRow = document.createElement('tr'), + containers = { + 'tr': document.createElement('tbody'), + 'tbody': table, 'thead': table, 'tfoot': table, + 'td': tableRow, 'th': tableRow, + '*': document.createElement('div') + }, + readyRE = /complete|loaded|interactive/, + simpleSelectorRE = /^[\w-]*$/, + class2type = {}, + toString = class2type.toString, + zepto = {}, + camelize, uniq, + tempParent = document.createElement('div'), + propMap = { + 'tabindex': 'tabIndex', + 'readonly': 'readOnly', + 'for': 'htmlFor', + 'class': 'className', + 'maxlength': 'maxLength', + 'cellspacing': 'cellSpacing', + 'cellpadding': 'cellPadding', + 'rowspan': 'rowSpan', + 'colspan': 'colSpan', + 'usemap': 'useMap', + 'frameborder': 'frameBorder', + 'contenteditable': 'contentEditable' + }, + isArray = Array.isArray || + function(object){ return object instanceof Array } + + zepto.matches = function(element, selector) { + if (!selector || !element || element.nodeType !== 1) return false + var matchesSelector = element.webkitMatchesSelector || element.mozMatchesSelector || + element.oMatchesSelector || element.matchesSelector + if (matchesSelector) return matchesSelector.call(element, selector) + // fall back to performing a selector: + var match, parent = element.parentNode, temp = !parent + if (temp) (parent = tempParent).appendChild(element) + match = ~zepto.qsa(parent, selector).indexOf(element) + temp && tempParent.removeChild(element) + return match + } + + function type(obj) { + return obj == null ? String(obj) : + class2type[toString.call(obj)] || "object" + } + + function isFunction(value) { return type(value) == "function" } + function isWindow(obj) { return obj != null && obj == obj.window } + function isDocument(obj) { return obj != null && obj.nodeType == obj.DOCUMENT_NODE } + function isObject(obj) { return type(obj) == "object" } + function isPlainObject(obj) { + return isObject(obj) && !isWindow(obj) && Object.getPrototypeOf(obj) == Object.prototype + } + function likeArray(obj) { return typeof obj.length == 'number' } + + function compact(array) { return filter.call(array, function(item){ return item != null }) } + function flatten(array) { return array.length > 0 ? $.fn.concat.apply([], array) : array } + camelize = function(str){ return str.replace(/-+(.)?/g, function(match, chr){ return chr ? chr.toUpperCase() : '' }) } + function dasherize(str) { + return str.replace(/::/g, '/') + .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') + .replace(/([a-z\d])([A-Z])/g, '$1_$2') + .replace(/_/g, '-') + .toLowerCase() + } + uniq = function(array){ return filter.call(array, function(item, idx){ return array.indexOf(item) == idx }) } + + function classRE(name) { + return name in classCache ? + classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)')) + } + + function maybeAddPx(name, value) { + return (typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value + } + + function defaultDisplay(nodeName) { + var element, display + if (!elementDisplay[nodeName]) { + element = document.createElement(nodeName) + document.body.appendChild(element) + display = getComputedStyle(element, '').getPropertyValue("display") + element.parentNode.removeChild(element) + display == "none" && (display = "block") + elementDisplay[nodeName] = display + } + return elementDisplay[nodeName] + } + + function children(element) { + return 'children' in element ? + slice.call(element.children) : + $.map(element.childNodes, function(node){ if (node.nodeType == 1) return node }) + } + + // `$.zepto.fragment` takes a html string and an optional tag name + // to generate DOM nodes nodes from the given html string. + // The generated DOM nodes are returned as an array. + // This function can be overriden in plugins for example to make + // it compatible with browsers that don't support the DOM fully. + zepto.fragment = function(html, name, properties) { + var dom, nodes, container + + // A special case optimization for a single tag + if (singleTagRE.test(html)) dom = $(document.createElement(RegExp.$1)) + + if (!dom) { + if (html.replace) html = html.replace(tagExpanderRE, "<$1>") + if (name === undefined) name = fragmentRE.test(html) && RegExp.$1 + if (!(name in containers)) name = '*' + + container = containers[name] + container.innerHTML = '' + html + dom = $.each(slice.call(container.childNodes), function(){ + container.removeChild(this) + }) + } + + if (isPlainObject(properties)) { + nodes = $(dom) + $.each(properties, function(key, value) { + if (methodAttributes.indexOf(key) > -1) nodes[key](value) + else nodes.attr(key, value) + }) + } + + return dom + } + + // `$.zepto.Z` swaps out the prototype of the given `dom` array + // of nodes with `$.fn` and thus supplying all the Zepto functions + // to the array. Note that `__proto__` is not supported on Internet + // Explorer. This method can be overriden in plugins. + zepto.Z = function(dom, selector) { + dom = dom || [] + dom.__proto__ = $.fn + dom.selector = selector || '' + return dom + } + + // `$.zepto.isZ` should return `true` if the given object is a Zepto + // collection. This method can be overriden in plugins. + zepto.isZ = function(object) { + return object instanceof zepto.Z + } + + // `$.zepto.init` is Zepto's counterpart to jQuery's `$.fn.init` and + // takes a CSS selector and an optional context (and handles various + // special cases). + // This method can be overriden in plugins. + zepto.init = function(selector, context) { + var dom + // If nothing given, return an empty Zepto collection + if (!selector) return zepto.Z() + // Optimize for string selectors + else if (typeof selector == 'string') { + selector = selector.trim() + // If it's a html fragment, create nodes from it + // Note: In both Chrome 21 and Firefox 15, DOM error 12 + // is thrown if the fragment doesn't begin with < + if (selector[0] == '<' && fragmentRE.test(selector)) + dom = zepto.fragment(selector, RegExp.$1, context), selector = null + // If there's a context, create a collection on that context first, and select + // nodes from there + else if (context !== undefined) return $(context).find(selector) + // If it's a CSS selector, use it to select nodes. + else dom = zepto.qsa(document, selector) + } + // If a function is given, call it when the DOM is ready + else if (isFunction(selector)) return $(document).ready(selector) + // If a Zepto collection is given, just return it + else if (zepto.isZ(selector)) return selector + else { + // normalize array if an array of nodes is given + if (isArray(selector)) dom = compact(selector) + // Wrap DOM nodes. + else if (isObject(selector)) + dom = [selector], selector = null + // If it's a html fragment, create nodes from it + else if (fragmentRE.test(selector)) + dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null + // If there's a context, create a collection on that context first, and select + // nodes from there + else if (context !== undefined) return $(context).find(selector) + // And last but no least, if it's a CSS selector, use it to select nodes. + else dom = zepto.qsa(document, selector) + } + // create a new Zepto collection from the nodes found + return zepto.Z(dom, selector) + } + + // `$` will be the base `Zepto` object. When calling this + // function just call `$.zepto.init, which makes the implementation + // details of selecting nodes and creating Zepto collections + // patchable in plugins. + $ = function(selector, context){ + return zepto.init(selector, context) + } + + function extend(target, source, deep) { + for (key in source) + if (deep && (isPlainObject(source[key]) || isArray(source[key]))) { + if (isPlainObject(source[key]) && !isPlainObject(target[key])) + target[key] = {} + if (isArray(source[key]) && !isArray(target[key])) + target[key] = [] + extend(target[key], source[key], deep) + } + else if (source[key] !== undefined) target[key] = source[key] + } + + // Copy all but undefined properties from one or more + // objects to the `target` object. + $.extend = function(target){ + var deep, args = slice.call(arguments, 1) + if (typeof target == 'boolean') { + deep = target + target = args.shift() + } + args.forEach(function(arg){ extend(target, arg, deep) }) + return target + } + + // `$.zepto.qsa` is Zepto's CSS selector implementation which + // uses `document.querySelectorAll` and optimizes for some special cases, like `#id`. + // This method can be overriden in plugins. + zepto.qsa = function(element, selector){ + var found, + maybeID = selector[0] == '#', + maybeClass = !maybeID && selector[0] == '.', + nameOnly = maybeID || maybeClass ? selector.slice(1) : selector, // Ensure that a 1 char tag name still gets checked + isSimple = simpleSelectorRE.test(nameOnly) + return (isDocument(element) && isSimple && maybeID) ? + ( (found = element.getElementById(nameOnly)) ? [found] : [] ) : + (element.nodeType !== 1 && element.nodeType !== 9) ? [] : + slice.call( + isSimple && !maybeID ? + maybeClass ? element.getElementsByClassName(nameOnly) : // If it's simple, it could be a class + element.getElementsByTagName(selector) : // Or a tag + element.querySelectorAll(selector) // Or it's not simple, and we need to query all + ) + } + + function filtered(nodes, selector) { + return selector == null ? $(nodes) : $(nodes).filter(selector) + } + + $.contains = function(parent, node) { + return parent !== node && parent.contains(node) + } + + function funcArg(context, arg, idx, payload) { + return isFunction(arg) ? arg.call(context, idx, payload) : arg + } + + function setAttribute(node, name, value) { + value == null ? node.removeAttribute(name) : node.setAttribute(name, value) + } + + // access className property while respecting SVGAnimatedString + function className(node, value){ + var klass = node.className, + svg = klass && klass.baseVal !== undefined + + if (value === undefined) return svg ? klass.baseVal : klass + svg ? (klass.baseVal = value) : (node.className = value) + } + + // "true" => true + // "false" => false + // "null" => null + // "42" => 42 + // "42.5" => 42.5 + // "08" => "08" + // JSON => parse if valid + // String => self + function deserializeValue(value) { + var num + try { + return value ? + value == "true" || + ( value == "false" ? false : + value == "null" ? null : + !/^0/.test(value) && !isNaN(num = Number(value)) ? num : + /^[\[\{]/.test(value) ? $.parseJSON(value) : + value ) + : value + } catch(e) { + return value + } + } + + $.type = type + $.isFunction = isFunction + $.isWindow = isWindow + $.isArray = isArray + $.isPlainObject = isPlainObject + + $.isEmptyObject = function(obj) { + var name + for (name in obj) return false + return true + } + + $.inArray = function(elem, array, i){ + return emptyArray.indexOf.call(array, elem, i) + } + + $.camelCase = camelize + $.trim = function(str) { + return str == null ? "" : String.prototype.trim.call(str) + } + + // plugin compatibility + $.uuid = 0 + $.support = { } + $.expr = { } + + $.map = function(elements, callback){ + var value, values = [], i, key + if (likeArray(elements)) + for (i = 0; i < elements.length; i++) { + value = callback(elements[i], i) + if (value != null) values.push(value) + } + else + for (key in elements) { + value = callback(elements[key], key) + if (value != null) values.push(value) + } + return flatten(values) + } + + $.each = function(elements, callback){ + var i, key + if (likeArray(elements)) { + for (i = 0; i < elements.length; i++) + if (callback.call(elements[i], i, elements[i]) === false) return elements + } else { + for (key in elements) + if (callback.call(elements[key], key, elements[key]) === false) return elements + } + + return elements + } + + $.grep = function(elements, callback){ + return filter.call(elements, callback) + } + + if (window.JSON) $.parseJSON = JSON.parse + + // Populate the class2type map + $.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase() + }) + + // Define methods that will be available on all + // Zepto collections + $.fn = { + // Because a collection acts like an array + // copy over these useful array functions. + forEach: emptyArray.forEach, + reduce: emptyArray.reduce, + push: emptyArray.push, + sort: emptyArray.sort, + indexOf: emptyArray.indexOf, + concat: emptyArray.concat, + + // `map` and `slice` in the jQuery API work differently + // from their array counterparts + map: function(fn){ + return $($.map(this, function(el, i){ return fn.call(el, i, el) })) + }, + slice: function(){ + return $(slice.apply(this, arguments)) + }, + + ready: function(callback){ + // need to check if document.body exists for IE as that browser reports + // document ready when it hasn't yet created the body element + if (readyRE.test(document.readyState) && document.body) callback($) + else document.addEventListener('DOMContentLoaded', function(){ callback($) }, false) + return this + }, + get: function(idx){ + return idx === undefined ? slice.call(this) : this[idx >= 0 ? idx : idx + this.length] + }, + toArray: function(){ return this.get() }, + size: function(){ + return this.length + }, + remove: function(){ + return this.each(function(){ + if (this.parentNode != null) + this.parentNode.removeChild(this) + }) + }, + each: function(callback){ + emptyArray.every.call(this, function(el, idx){ + return callback.call(el, idx, el) !== false + }) + return this + }, + filter: function(selector){ + if (isFunction(selector)) return this.not(this.not(selector)) + return $(filter.call(this, function(element){ + return zepto.matches(element, selector) + })) + }, + add: function(selector,context){ + return $(uniq(this.concat($(selector,context)))) + }, + is: function(selector){ + return this.length > 0 && zepto.matches(this[0], selector) + }, + not: function(selector){ + var nodes=[] + if (isFunction(selector) && selector.call !== undefined) + this.each(function(idx){ + if (!selector.call(this,idx)) nodes.push(this) + }) + else { + var excludes = typeof selector == 'string' ? this.filter(selector) : + (likeArray(selector) && isFunction(selector.item)) ? slice.call(selector) : $(selector) + this.forEach(function(el){ + if (excludes.indexOf(el) < 0) nodes.push(el) + }) + } + return $(nodes) + }, + has: function(selector){ + return this.filter(function(){ + return isObject(selector) ? + $.contains(this, selector) : + $(this).find(selector).size() + }) + }, + eq: function(idx){ + return idx === -1 ? this.slice(idx) : this.slice(idx, + idx + 1) + }, + first: function(){ + var el = this[0] + return el && !isObject(el) ? el : $(el) + }, + last: function(){ + var el = this[this.length - 1] + return el && !isObject(el) ? el : $(el) + }, + find: function(selector){ + var result, $this = this + if (typeof selector == 'object') + result = $(selector).filter(function(){ + var node = this + return emptyArray.some.call($this, function(parent){ + return $.contains(parent, node) + }) + }) + else if (this.length == 1) result = $(zepto.qsa(this[0], selector)) + else result = this.map(function(){ return zepto.qsa(this, selector) }) + return result + }, + closest: function(selector, context){ + var node = this[0], collection = false + if (typeof selector == 'object') collection = $(selector) + while (node && !(collection ? collection.indexOf(node) >= 0 : zepto.matches(node, selector))) + node = node !== context && !isDocument(node) && node.parentNode + return $(node) + }, + parents: function(selector){ + var ancestors = [], nodes = this + while (nodes.length > 0) + nodes = $.map(nodes, function(node){ + if ((node = node.parentNode) && !isDocument(node) && ancestors.indexOf(node) < 0) { + ancestors.push(node) + return node + } + }) + return filtered(ancestors, selector) + }, + parent: function(selector){ + return filtered(uniq(this.pluck('parentNode')), selector) + }, + children: function(selector){ + return filtered(this.map(function(){ return children(this) }), selector) + }, + contents: function() { + return this.map(function() { return slice.call(this.childNodes) }) + }, + siblings: function(selector){ + return filtered(this.map(function(i, el){ + return filter.call(children(el.parentNode), function(child){ return child!==el }) + }), selector) + }, + empty: function(){ + return this.each(function(){ this.innerHTML = '' }) + }, + // `pluck` is borrowed from Prototype.js + pluck: function(property){ + return $.map(this, function(el){ return el[property] }) + }, + show: function(){ + return this.each(function(){ + this.style.display == "none" && (this.style.display = '') + if (getComputedStyle(this, '').getPropertyValue("display") == "none") + this.style.display = defaultDisplay(this.nodeName) + }) + }, + replaceWith: function(newContent){ + return this.before(newContent).remove() + }, + wrap: function(structure){ + var func = isFunction(structure) + if (this[0] && !func) + var dom = $(structure).get(0), + clone = dom.parentNode || this.length > 1 + + return this.each(function(index){ + $(this).wrapAll( + func ? structure.call(this, index) : + clone ? dom.cloneNode(true) : dom + ) + }) + }, + wrapAll: function(structure){ + if (this[0]) { + $(this[0]).before(structure = $(structure)) + var children + // drill down to the inmost element + while ((children = structure.children()).length) structure = children.first() + $(structure).append(this) + } + return this + }, + wrapInner: function(structure){ + var func = isFunction(structure) + return this.each(function(index){ + var self = $(this), contents = self.contents(), + dom = func ? structure.call(this, index) : structure + contents.length ? contents.wrapAll(dom) : self.append(dom) + }) + }, + unwrap: function(){ + this.parent().each(function(){ + $(this).replaceWith($(this).children()) + }) + return this + }, + clone: function(){ + return this.map(function(){ return this.cloneNode(true) }) + }, + hide: function(){ + return this.css("display", "none") + }, + toggle: function(setting){ + return this.each(function(){ + var el = $(this) + ;(setting === undefined ? el.css("display") == "none" : setting) ? el.show() : el.hide() + }) + }, + prev: function(selector){ return $(this.pluck('previousElementSibling')).filter(selector || '*') }, + next: function(selector){ return $(this.pluck('nextElementSibling')).filter(selector || '*') }, + html: function(html){ + return arguments.length === 0 ? + (this.length > 0 ? this[0].innerHTML : null) : + this.each(function(idx){ + var originHtml = this.innerHTML + $(this).empty().append( funcArg(this, html, idx, originHtml) ) + }) + }, + text: function(text){ + return arguments.length === 0 ? + (this.length > 0 ? this[0].textContent : null) : + this.each(function(){ this.textContent = (text === undefined) ? '' : ''+text }) + }, + attr: function(name, value){ + var result + return (typeof name == 'string' && value === undefined) ? + (this.length == 0 || this[0].nodeType !== 1 ? undefined : + (name == 'value' && this[0].nodeName == 'INPUT') ? this.val() : + (!(result = this[0].getAttribute(name)) && name in this[0]) ? this[0][name] : result + ) : + this.each(function(idx){ + if (this.nodeType !== 1) return + if (isObject(name)) for (key in name) setAttribute(this, key, name[key]) + else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name))) + }) + }, + removeAttr: function(name){ + return this.each(function(){ this.nodeType === 1 && setAttribute(this, name) }) + }, + prop: function(name, value){ + name = propMap[name] || name + return (value === undefined) ? + (this[0] && this[0][name]) : + this.each(function(idx){ + this[name] = funcArg(this, value, idx, this[name]) + }) + }, + data: function(name, value){ + var data = this.attr('data-' + name.replace(capitalRE, '-$1').toLowerCase(), value) + return data !== null ? deserializeValue(data) : undefined + }, + val: function(value){ + return arguments.length === 0 ? + (this[0] && (this[0].multiple ? + $(this[0]).find('option').filter(function(){ return this.selected }).pluck('value') : + this[0].value) + ) : + this.each(function(idx){ + this.value = funcArg(this, value, idx, this.value) + }) + }, + offset: function(coordinates){ + if (coordinates) return this.each(function(index){ + var $this = $(this), + coords = funcArg(this, coordinates, index, $this.offset()), + parentOffset = $this.offsetParent().offset(), + props = { + top: coords.top - parentOffset.top, + left: coords.left - parentOffset.left + } + + if ($this.css('position') == 'static') props['position'] = 'relative' + $this.css(props) + }) + if (this.length==0) return null + var obj = this[0].getBoundingClientRect() + return { + left: obj.left + window.pageXOffset, + top: obj.top + window.pageYOffset, + width: Math.round(obj.width), + height: Math.round(obj.height) + } + }, + css: function(property, value){ + if (arguments.length < 2) { + var element = this[0], computedStyle = getComputedStyle(element, '') + if(!element) return + if (typeof property == 'string') + return element.style[camelize(property)] || computedStyle.getPropertyValue(property) + else if (isArray(property)) { + var props = {} + $.each(isArray(property) ? property: [property], function(_, prop){ + props[prop] = (element.style[camelize(prop)] || computedStyle.getPropertyValue(prop)) + }) + return props + } + } + + var css = '' + if (type(property) == 'string') { + if (!value && value !== 0) + this.each(function(){ this.style.removeProperty(dasherize(property)) }) + else + css = dasherize(property) + ":" + maybeAddPx(property, value) + } else { + for (key in property) + if (!property[key] && property[key] !== 0) + this.each(function(){ this.style.removeProperty(dasherize(key)) }) + else + css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';' + } + + return this.each(function(){ this.style.cssText += ';' + css }) + }, + index: function(element){ + return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0]) + }, + hasClass: function(name){ + if (!name) return false + return emptyArray.some.call(this, function(el){ + return this.test(className(el)) + }, classRE(name)) + }, + addClass: function(name){ + if (!name) return this + return this.each(function(idx){ + classList = [] + var cls = className(this), newName = funcArg(this, name, idx, cls) + newName.split(/\s+/g).forEach(function(klass){ + if (!$(this).hasClass(klass)) classList.push(klass) + }, this) + classList.length && className(this, cls + (cls ? " " : "") + classList.join(" ")) + }) + }, + removeClass: function(name){ + return this.each(function(idx){ + if (name === undefined) return className(this, '') + classList = className(this) + funcArg(this, name, idx, classList).split(/\s+/g).forEach(function(klass){ + classList = classList.replace(classRE(klass), " ") + }) + className(this, classList.trim()) + }) + }, + toggleClass: function(name, when){ + if (!name) return this + return this.each(function(idx){ + var $this = $(this), names = funcArg(this, name, idx, className(this)) + names.split(/\s+/g).forEach(function(klass){ + (when === undefined ? !$this.hasClass(klass) : when) ? + $this.addClass(klass) : $this.removeClass(klass) + }) + }) + }, + scrollTop: function(value){ + if (!this.length) return + var hasScrollTop = 'scrollTop' in this[0] + if (value === undefined) return hasScrollTop ? this[0].scrollTop : this[0].pageYOffset + return this.each(hasScrollTop ? + function(){ this.scrollTop = value } : + function(){ this.scrollTo(this.scrollX, value) }) + }, + scrollLeft: function(value){ + if (!this.length) return + var hasScrollLeft = 'scrollLeft' in this[0] + if (value === undefined) return hasScrollLeft ? this[0].scrollLeft : this[0].pageXOffset + return this.each(hasScrollLeft ? + function(){ this.scrollLeft = value } : + function(){ this.scrollTo(value, this.scrollY) }) + }, + position: function() { + if (!this.length) return + + var elem = this[0], + // Get *real* offsetParent + offsetParent = this.offsetParent(), + // Get correct offsets + offset = this.offset(), + parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset() + + // Subtract element margins + // note: when an element has margin: auto the offsetLeft and marginLeft + // are the same in Safari causing offset.left to incorrectly be 0 + offset.top -= parseFloat( $(elem).css('margin-top') ) || 0 + offset.left -= parseFloat( $(elem).css('margin-left') ) || 0 + + // Add offsetParent borders + parentOffset.top += parseFloat( $(offsetParent[0]).css('border-top-width') ) || 0 + parentOffset.left += parseFloat( $(offsetParent[0]).css('border-left-width') ) || 0 + + // Subtract the two offsets + return { + top: offset.top - parentOffset.top, + left: offset.left - parentOffset.left + } + }, + offsetParent: function() { + return this.map(function(){ + var parent = this.offsetParent || document.body + while (parent && !rootNodeRE.test(parent.nodeName) && $(parent).css("position") == "static") + parent = parent.offsetParent + return parent + }) + } + } + + // for now + $.fn.detach = $.fn.remove + + // Generate the `width` and `height` functions + ;['width', 'height'].forEach(function(dimension){ + var dimensionProperty = + dimension.replace(/./, function(m){ return m[0].toUpperCase() }) + + $.fn[dimension] = function(value){ + var offset, el = this[0] + if (value === undefined) return isWindow(el) ? el['inner' + dimensionProperty] : + isDocument(el) ? el.documentElement['scroll' + dimensionProperty] : + (offset = this.offset()) && offset[dimension] + else return this.each(function(idx){ + el = $(this) + el.css(dimension, funcArg(this, value, idx, el[dimension]())) + }) + } + }) + + function traverseNode(node, fun) { + fun(node) + for (var key in node.childNodes) traverseNode(node.childNodes[key], fun) + } + + // Generate the `after`, `prepend`, `before`, `append`, + // `insertAfter`, `insertBefore`, `appendTo`, and `prependTo` methods. + adjacencyOperators.forEach(function(operator, operatorIndex) { + var inside = operatorIndex % 2 //=> prepend, append + + $.fn[operator] = function(){ + // arguments can be nodes, arrays of nodes, Zepto objects and HTML strings + var argType, nodes = $.map(arguments, function(arg) { + argType = type(arg) + return argType == "object" || argType == "array" || arg == null ? + arg : zepto.fragment(arg) + }), + parent, copyByClone = this.length > 1 + if (nodes.length < 1) return this + + return this.each(function(_, target){ + parent = inside ? target : target.parentNode + + // convert all methods to a "before" operation + target = operatorIndex == 0 ? target.nextSibling : + operatorIndex == 1 ? target.firstChild : + operatorIndex == 2 ? target : + null + + nodes.forEach(function(node){ + if (copyByClone) node = node.cloneNode(true) + else if (!parent) return $(node).remove() + + traverseNode(parent.insertBefore(node, target), function(el){ + if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' && + (!el.type || el.type === 'text/javascript') && !el.src) + window['eval'].call(window, el.innerHTML) + }) + }) + }) + } + + // after => insertAfter + // prepend => prependTo + // before => insertBefore + // append => appendTo + $.fn[inside ? operator+'To' : 'insert'+(operatorIndex ? 'Before' : 'After')] = function(html){ + $(html)[operator](this) + return this + } + }) + + zepto.Z.prototype = $.fn + + // Export internal API functions in the `$.zepto` namespace + zepto.uniq = uniq + zepto.deserializeValue = deserializeValue + $.zepto = zepto + + return $ +})() + +// If `$` is not yet defined, point it to `Zepto` +window.Zepto = Zepto +window.$ === undefined && (window.$ = Zepto) diff --git a/lib/zeptojs/test/ajax.html b/lib/zeptojs/test/ajax.html new file mode 100644 index 0000000..a7f18be --- /dev/null +++ b/lib/zeptojs/test/ajax.html @@ -0,0 +1,1433 @@ + + + + + + + Zepto Ajax unit tests + + + + + +

Zepto Ajax unit tests

+

+ Running… see browser console for results +

+
+
+
+ + + + diff --git a/lib/zeptojs/test/ajax_deferred.html b/lib/zeptojs/test/ajax_deferred.html new file mode 100644 index 0000000..e1b25df --- /dev/null +++ b/lib/zeptojs/test/ajax_deferred.html @@ -0,0 +1,213 @@ + + + + + + + Zepto Ajax deferred + + + + + +

Zepto Ajax deferred

+

+ Running… see browser console for results +

+
+
+ + + + diff --git a/lib/zeptojs/test/callbacks.html b/lib/zeptojs/test/callbacks.html new file mode 100644 index 0000000..4a7388e --- /dev/null +++ b/lib/zeptojs/test/callbacks.html @@ -0,0 +1,272 @@ + + + + + + + Zepto Callbacks unit tests + + + + + +

Zepto Callbacks unit tests

+

+ Running… see browser console for results +

+ + + + diff --git a/lib/zeptojs/test/data.html b/lib/zeptojs/test/data.html new file mode 100644 index 0000000..294fda1 --- /dev/null +++ b/lib/zeptojs/test/data.html @@ -0,0 +1,329 @@ + + + + + + + Zepto Data unit tests + + + + + +

Zepto Data unit tests

+

+ Running… see browser console for results +

+
+
+
+
+
    +
  1. +
  2. +
+
    +
  1. +
  2. +
+
+ + + + diff --git a/lib/zeptojs/test/deferred.html b/lib/zeptojs/test/deferred.html new file mode 100644 index 0000000..7b3dba6 --- /dev/null +++ b/lib/zeptojs/test/deferred.html @@ -0,0 +1,418 @@ + + + + + + + Zepto Deferred unit tests + + + + + +

Zepto Deferred unit tests

+

+ Running… see browser console for results +

+ + + + diff --git a/lib/zeptojs/test/detect.html b/lib/zeptojs/test/detect.html new file mode 100644 index 0000000..e082430 --- /dev/null +++ b/lib/zeptojs/test/detect.html @@ -0,0 +1,432 @@ + + + + + + + Zepto Detect unit tests + + + + + +

Zepto Detect unit tests

+

+ Running… see browser console for results +

+ + + + diff --git a/lib/zeptojs/test/event.html b/lib/zeptojs/test/event.html new file mode 100644 index 0000000..3db7372 --- /dev/null +++ b/lib/zeptojs/test/event.html @@ -0,0 +1,434 @@ + + + + + + + Zepto Event unit tests + + + + + +

Zepto Event unit tests

+

+ Running… see browser console for results +

+
+
+ + + + diff --git a/lib/zeptojs/test/evidence_runner.js b/lib/zeptojs/test/evidence_runner.js new file mode 100644 index 0000000..7c13f66 --- /dev/null +++ b/lib/zeptojs/test/evidence_runner.js @@ -0,0 +1,123 @@ +(function(){ + var ConsoleTestRunner = Evidence.AutoRunner.RUNNERS.console, + ConsoleTestResult = Evidence.UI.Console.TestResult, + AutoRunner = Evidence.AutoRunner, + printf = Evidence.UI.printf + + function inherit(superclass, extra) { + var klass = function(){ + this.initialize.apply(this, arguments) + } + var tmp = function(){} + tmp.prototype = superclass.prototype + klass.prototype = new tmp() + klass.prototype.constructor = klass + klass.prototype.initialize = function(){ + superclass.apply(this, arguments) + } + + if (extra) { + var methods = extra.call(klass, superclass.prototype) + for (var method in methods) klass.prototype[method] = methods[method] + } + return klass + } + + var TestRunner = inherit(ConsoleTestRunner, function(_super) { + AutoRunner.RUNNERS.zepto = this + return { + _makeResult: function() { return new TestResult(this.logger) } + } + }) + + var TestResult = inherit(ConsoleTestResult, function(_super) { + return { + start: function(t0) { + Evidence.TestResult.prototype.start.call(this, t0) + this.logger.debug('Started tests.') + }, + stop: function(t1) { + _super.stop.call(this, t1) + displayResults(this, (t1-this.t0)/1000) + checkLeakedGlobals() + }, + pauseTest: function(testcase) { + this.logger.debug('Paused testcase ' + testcase + '.') + }, + restartTest: function(testcase) { + this.logger.debug('Restarted testcase ' + testcase + '.') + }, + startSuite: function(suite) { + this.logger.debug('Started suite ' + suite + '.') + }, + addFailure: function(testcase, reason) { + this.logToServer(testcase, reason) + _super.addFailure.call(this, testcase, reason) + }, + addError: function(testcase, error) { + this.logToServer(testcase, error) + _super.addError.call(this, testcase, error) + }, + logToServer: function(testcase, error) { + if (location.protocol == 'file:') return + var name = testcase.parent.name + '#' + testcase.name, + message = error.template ? + Evidence.UI.printf(error.template, error.args) + (error.message ? ' ' + error.message : '') : + error.message, + body = 'name=' + encodeURIComponent(name) + '&' + + 'message=' + encodeURIComponent(message) + '&' + + 'trace=' + encodeURIComponent(error.stack || '') + + var xhr = new XMLHttpRequest() + xhr.open('POST', '/test/log', true) + xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded') + xhr.send(body) + } + } + }) + + Evidence.TestCase.prototype.debug = function(message) { + this._result.logToServer(this, { message: message }) + } + + // HACK: force our test runner as default + ;(function(){ + var _super = AutoRunner.prototype.retrieveOptions + AutoRunner.prototype.retrieveOptions = function() { + var options = _super.call(this) + if (!options.runner) options.runner = 'zepto' + return options + } + })() + + function $(id) { return document.getElementById(id) } + + function displayResults(results, seconds) { + var container = $('results') + if (container) { + if (results.failureCount || results.errorCount) { + container.className = 'failed' + container.innerHTML = printf("Finished in %d s. – %d failures, %d errors (%d assertions)", + [seconds, results.failureCount, results.errorCount, results.assertionCount]) + } else { + container.className = 'passed' + container.innerHTML = printf("Finished in %d s. – %d tests passed (%d assertions)", + [seconds, results.testCount, results.assertionCount]) + } + container.className += ' finished' + } + } + + var globals = [], expected = ['Zepto', '$', 'Evidence'] + for (var key in window) globals.push(key) + + function checkLeakedGlobals() { + var opera = /^Opera\b/.test(navigator.userAgent) + for (var key in window) + if ( globals.indexOf(key) < 0 && expected.indexOf(key) < 0 && + (!opera || typeof window[key] != 'object' || window[key].id != key) && + window.console && console.warn + ) + console.warn("unexpected global: " + key) + } +})() diff --git a/lib/zeptojs/test/fixtures/ajax_load_javascript.js b/lib/zeptojs/test/fixtures/ajax_load_javascript.js new file mode 100644 index 0000000..e085da6 --- /dev/null +++ b/lib/zeptojs/test/fixtures/ajax_load_javascript.js @@ -0,0 +1 @@ +window.testValue = 1; diff --git a/lib/zeptojs/test/fixtures/ajax_load_selector.html b/lib/zeptojs/test/fixtures/ajax_load_selector.html new file mode 100644 index 0000000..5dcdaad --- /dev/null +++ b/lib/zeptojs/test/fixtures/ajax_load_selector.html @@ -0,0 +1,13 @@ + + + + + testAjaxLoad + + +
ajax load with selector
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +
+ + diff --git a/lib/zeptojs/test/fixtures/ajax_load_selector_javascript.html b/lib/zeptojs/test/fixtures/ajax_load_selector_javascript.html new file mode 100644 index 0000000..2d90add --- /dev/null +++ b/lib/zeptojs/test/fixtures/ajax_load_selector_javascript.html @@ -0,0 +1,16 @@ + + + + + testAjaxLoad + + +
ajax load with selector
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +
+ + + diff --git a/lib/zeptojs/test/fixtures/ajax_load_simple.html b/lib/zeptojs/test/fixtures/ajax_load_simple.html new file mode 100644 index 0000000..d859adb --- /dev/null +++ b/lib/zeptojs/test/fixtures/ajax_load_simple.html @@ -0,0 +1 @@ +simple ajax load diff --git a/lib/zeptojs/test/fixtures/iframe_document.html b/lib/zeptojs/test/fixtures/iframe_document.html new file mode 100644 index 0000000..1e8bf4f --- /dev/null +++ b/lib/zeptojs/test/fixtures/iframe_document.html @@ -0,0 +1 @@ +Hello from iframe! diff --git a/lib/zeptojs/test/fixtures/zepto.json b/lib/zeptojs/test/fixtures/zepto.json new file mode 100644 index 0000000..d7c2b76 --- /dev/null +++ b/lib/zeptojs/test/fixtures/zepto.json @@ -0,0 +1,3 @@ +{ + "zepto": "awesomeness" +} diff --git a/lib/zeptojs/test/form.html b/lib/zeptojs/test/form.html new file mode 100644 index 0000000..aaa612b --- /dev/null +++ b/lib/zeptojs/test/form.html @@ -0,0 +1,151 @@ + + + + + + + Zepto Form unit tests + + + + + +

Zepto Form unit tests

+

+ Running… see browser console for results +

+ +
+
+ + + + + + + + +
+ + + +
+ + + +
+ + + + + + +
+
+ + +
+ + + + diff --git a/lib/zeptojs/test/functional/assets.html b/lib/zeptojs/test/functional/assets.html new file mode 100644 index 0000000..528723e --- /dev/null +++ b/lib/zeptojs/test/functional/assets.html @@ -0,0 +1,61 @@ + + + + + Zepto assets functional test + + + + + + + +

Zepto assets functional test

+ +
+ + + +

When you run this test without the assets plugin, Mobile Safari will stop loading after on average 8 images (that's on the iPad with 1 MB images). It might also crash.

+

PLEASE NOTE: You must restart Safari between runs (click the home button to return to the home screen, double-click the home button, tap-and-hold the Safari icon, then tap the minus badge).

+ +
+ + + + diff --git a/lib/zeptojs/test/functional/fx.html b/lib/zeptojs/test/functional/fx.html new file mode 100644 index 0000000..5c50a4d --- /dev/null +++ b/lib/zeptojs/test/functional/fx.html @@ -0,0 +1,48 @@ + + + + + + + Zepto fx functional test + + + + + + + + +

Zepto fx functional

+ +
+
I ♥ fx
+
+ +
+
I ♥ fx
+
+ + + + + + + +
+ + + + + +
+ + + + diff --git a/lib/zeptojs/test/functional/gesture.html b/lib/zeptojs/test/functional/gesture.html new file mode 100644 index 0000000..c8513a9 --- /dev/null +++ b/lib/zeptojs/test/functional/gesture.html @@ -0,0 +1,38 @@ + + + + + Zepto gesture functional test + + + + + + + + + +

Zepto gestures functional test

+ +
+ gesture events test +
+ +
+ + + + diff --git a/lib/zeptojs/test/functional/touch.html b/lib/zeptojs/test/functional/touch.html new file mode 100644 index 0000000..cfd3032 --- /dev/null +++ b/lib/zeptojs/test/functional/touch.html @@ -0,0 +1,107 @@ + + + + + Zepto touch functional test + + + + + + + + +

Zepto touch functional test

+ +

+ Browser supports touch events? +
+ + Browser supports MS pointer events? + +

+ +
+ touch events test +
+ +
+ touch events test (scrollable cancel) +
+ +
+ touch events test (scrollable cancel, single tap only) +
+ +
+ + + + diff --git a/lib/zeptojs/test/functional/touchcancel.html b/lib/zeptojs/test/functional/touchcancel.html new file mode 100644 index 0000000..62535bb --- /dev/null +++ b/lib/zeptojs/test/functional/touchcancel.html @@ -0,0 +1,60 @@ + + + + + Zepto touch functional test + + + + + + + + +

Zepto touch functional test

+ +

+ Double-tap and hold until the JavaScript alert occurs. After closing the + alert, tap again. Without touchcancel you will see a double tap instead of a tap. +

+ +
+ touch events test +
+ +
+ + + + diff --git a/lib/zeptojs/test/fx.html b/lib/zeptojs/test/fx.html new file mode 100644 index 0000000..fe05d3c --- /dev/null +++ b/lib/zeptojs/test/fx.html @@ -0,0 +1,312 @@ + + + + + + + Zepto Fx unit tests + + + + + + +

Zepto Fx unit tests

+

+ Running… see browser console for results +

+ +
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + + diff --git a/lib/zeptojs/test/index.html b/lib/zeptojs/test/index.html new file mode 100644 index 0000000..c930fd3 --- /dev/null +++ b/lib/zeptojs/test/index.html @@ -0,0 +1,70 @@ + + + + + + + Zepto tests + + +

Zepto tests

+

+ This file is provided for manual testing. If there's a test failure, + please report the user agent: + +

+

+ Start a test server with (you'll need to have npm installed):
+
+ $ npm install (if you haven't installed dependencies yet)
+ $ npm run-script start +

+ On your device, go to
+ http://your-ip-address:3000/test +

+

+ To start a test, click or tap it. Use the back button of + your browser to return to this index page. + Functional tests may require manual interaction. +

+ +

Default modules unit tests

+ + +

Other modules unit test

+ + +

Functional tests

+ + + + diff --git a/lib/zeptojs/test/ios3.html b/lib/zeptojs/test/ios3.html new file mode 100644 index 0000000..9e3354a --- /dev/null +++ b/lib/zeptojs/test/ios3.html @@ -0,0 +1,62 @@ + + + + + + + Zepto iOS3 unit tests + + + + +

Zepto iOS3 unit tests

+

+ Running… see browser console for results +

+ + + diff --git a/lib/zeptojs/test/runner.coffee b/lib/zeptojs/test/runner.coffee new file mode 100644 index 0000000..f4a1610 --- /dev/null +++ b/lib/zeptojs/test/runner.coffee @@ -0,0 +1,72 @@ +# Test runner for PhantomJS +# Usage: +# $ phantomjs test/runner.coffee [, , ...] +# +# When no test pages specified, runs all automated tests. + +system = require('system') +fs = require('fs') + +args = system.args.slice(1) +prefix = args.shift() || "file://#{fs.workingDirectory}/" + +if args.length > 0 + # list of test pages to run + suites = args +else + # by default, run all test/*.html pages + modules = 'zepto ajax callbacks data deferred ajax_deferred detect touch event form fx selector stack'.split /\s+/ + suites = modules.map (name)-> "test/#{name}.html" + +page = require('webpage').create() + +page.onConsoleMessage = (msg) -> + console.log msg + +page.onError = (msg, trace) -> + console.log 'ERROR: ' + msg + +# used for waiting until the tests finish running +waitFor = (testFn, onReady, timeout=30000) -> + start = new Date() + interval = setInterval -> + if testFn() + clearInterval interval + onReady() + else if new Date() - start > timeout + console.log "timed out." + phantom.exit(1) + , 100 + +loadNextSuite = -> + if not suites.length + phantom.exit() + else + url = suites.shift() + "?verbosity=WARN" + # PhantomJS chokes on the query string on relative paths + url = prefix + url if not /:\/\//.test url + + page.open url, (status) -> + if status isnt "success" + console.log "failed opening #{url}" + phantom.exit(1) + + waitFor -> + page.evaluate -> + # the "#results" element needs to have the "finished" class + res = document.getElementById 'results' + /finished/.test res.className if res + , -> + passed = page.evaluate -> + res = document.getElementById 'results' + paths = location.pathname.split('/') + # echo test results to the console + console.log "#{paths[paths.length - 1]} - " + res.textContent + /passed/.test res.className + + if passed + loadNextSuite() + else + phantom.exit(1) + +loadNextSuite() diff --git a/lib/zeptojs/test/selector.html b/lib/zeptojs/test/selector.html new file mode 100644 index 0000000..1b5e0e6 --- /dev/null +++ b/lib/zeptojs/test/selector.html @@ -0,0 +1,91 @@ + + + + + + + Zepto Selector unit tests + + + + + + + +

Zepto Selector unit tests

+

+ Running… see browser console for results +

+
+
  • one
  • two
+
look at me!
+ +
    +
  1. child1
  2. +
  3. child2 +
      +
    • child3
    • +
    • child4
    • +
    +
  4. +
+
+ + + + diff --git a/lib/zeptojs/test/server.coffee b/lib/zeptojs/test/server.coffee new file mode 100644 index 0000000..1da075d --- /dev/null +++ b/lib/zeptojs/test/server.coffee @@ -0,0 +1,125 @@ +express = require 'express' +app = express() +path = require 'path' + +module.exports = app + +project_root = path.resolve(__dirname, '..') +app.use express.static(project_root) +app.use express.static(project_root + 'node_modules/mocha') +app.use express.static(project_root + 'node_modules/chai') + +app.use express.bodyParser() + +mime = (req) -> + type = req.headers['content-type'] or '' + type.split(';')[0] + +dump = (obj) -> + obj = '' unless obj + obj = JSON.stringify(obj) if obj and typeof obj isnt "string" + obj + +cleanTrace = (traceStr) -> + trace = traceStr.split("\n") + filtered = [] + for line in trace + if /\.html:/.test line + line = line.replace(/^.+?@/, '') + line = line.replace(/http:\/\/.+?\//, '') + line = line.replace(/(:\d+):\d+$/, '$1') + filtered.push line + filtered + +browser = (ua) -> + if m = ua.match /(Android .+?);/ + m[1] + else if m = ua.match /(iPhone|iPad|iPod).*?OS ([\d_]+)/ + 'iOS ' + m[2].replace(/_/g, '.') + else if m = ua.match /(Chrome\/[\d.]+)/ + m[1].replace '/', ' ' + else if m = ua.match /(Safari\/[\d.]+)/ + m[1].replace '/', ' ' + else if m = ua.match /(Firefox\/[\d.]+)/ + m[1].replace '/', ' ' + else if m = ua.match /\bMS(IE [\d.]+)/ + m[1] + else + ua + +app.all '/', (req, res) -> + res.redirect '/test' + +app.all '/test/echo=?', (req, res) -> + res.set 'Cache-Control', 'no-cache' + res.send """ + #{req.method} ?#{dump(req.query)} + content-type: #{mime(req)} + accept: #{req.headers['accept']} + #{dump(req.body)} + """ + +app.get '/test/jsonp', (req, res) -> + res.jsonp + query: req.query + hello: 'world' + +# send JSONP response despite callback not being set +app.get '/test/jsonpBlah', (req, res) -> + res.set 'Content-Type', 'text/javascript' + res.send 'blah()' + +app.get '/test/json', (req, res) -> + res.set 'Cache-Control', 'no-cache' + expectedType = req.headers['accept'] + if expectedType is '*/*' or /json/.test expectedType + if req.query.invalid + res.set 'Content-Type', 'application/json' + res.send 'invalidJSON' + else + res.json + query: req.query + hello: 'world' + else + res.send 400, 'FAIL' + +app.post '/test/create', (req, res) -> + res.json + action: 'created' + query: req.query + payload: req.body + +app.all '/test/slow', (req, res) -> + setTimeout -> + res.send 'DONE' + , 200 + +app.get '/test/cached', (req, res) -> + res.set 'Cache-Control', 'max-age=2' + res.set 'Expires', new Date(Date.now() + 2000).toUTCString() + now = new Date() + res.send now.getTime().toString() + +app.get '/test/auth', (req, res) -> + if req.headers.authorization is 'Basic emVwdG86ZG9nZQ==' + res.send 200 + else + res.set 'WWW-Authenticate', "Basic realm=\"#{req.query.realm}\"" + res.send 401 + +app.post '/test/log', (req, res) -> + params = req.body + trace = cleanTrace params.trace + console.log "[%s] %s: %s", browser(req.headers['user-agent']), params.name, params.message + console.log trace.join("\n").replace(/^/mg, ' ') if trace.length + res.send 200 + +app.all '/test/error', (req, res) -> + res.send 500, 'BOOM' + +if process.argv[1] is __filename + port = process.argv[2] + unless port + port = 3000 + console.log "Listening on port #{port}" + app.listen port diff --git a/lib/zeptojs/test/stack.html b/lib/zeptojs/test/stack.html new file mode 100644 index 0000000..d4fb2e7 --- /dev/null +++ b/lib/zeptojs/test/stack.html @@ -0,0 +1,73 @@ + + + + + + + Zepto Stack unit tests + + + + + +

Zepto Stack unit tests

+

+ Running… see browser console for results +

+
+ +
+
+
12
+
+ +
+
+
+
+
+
+ +
+ + + + diff --git a/lib/zeptojs/test/test.css b/lib/zeptojs/test/test.css new file mode 100644 index 0000000..98e77a0 --- /dev/null +++ b/lib/zeptojs/test/test.css @@ -0,0 +1,61 @@ +* { + font-family: -apple-system-font, Helvetica, sans-serif; +} +body { + padding: 1em; +} +a:link, a:visited { color: darkblue; } +#results.failed em { font-style: normal; color: crimson; } +#results.passed em { font-style: normal; color: darkgreen; } +#fixtures { + position: absolute; + top: -10000px; + left: -10000px; +} +#nav { + list-style: none; + padding: 0; + margin: 2em 0 0 0; + font-size: 12px; + max-width: 30em; +} +#nav a { + padding: .8em 1.5em; + background: #eee; + border-top: 2px solid white; + text-decoration: none; + text-transform: uppercase; + letter-spacing: .1em; + display: block; + color: #555; +} +#nav .current a { background: #ccc; color: black; } +#nav a:active { background: #555; color: white; } + +ul { + padding: 0; +} + +li { + list-style-type: none; + padding: 0 0 15px; +} + +li a { + font-size: 16px; + border: 1px solid darkblue; + padding: 3px 10px; + text-decoration: none; +} +li a:visited { + border-color: #888; + color: #aaa; +} + +@media only screen and (max-device-width:480px) { + body { margin: 7px; font-size: small; } + h1 { margin: 0; } + h1, #results { text-align: center; } + #results { min-height: 3em; } + #nav { font-size: 14px; max-width: auto; } +} diff --git a/lib/zeptojs/test/touch.html b/lib/zeptojs/test/touch.html new file mode 100644 index 0000000..90829e4 --- /dev/null +++ b/lib/zeptojs/test/touch.html @@ -0,0 +1,338 @@ + + + + + + + Zepto Touch unit tests + + + + + +

Zepto Touch unit tests

+

+ Running… see browser console for results +

+ + + + + + diff --git a/lib/zeptojs/test/zepto.html b/lib/zeptojs/test/zepto.html new file mode 100644 index 0000000..658c7f6 --- /dev/null +++ b/lib/zeptojs/test/zepto.html @@ -0,0 +1,3105 @@ + + + + + + + Zepto Core unit tests + + + + + + +

Zepto Core unit tests

+

+ Running… see browser console for results +

+
+ +
+

+ yay + + nay +

+ +
+ +
+
Derp
+
+ +
+
Hello
+
And
+
Goodbye
+
+ +
+
+
+
+ +
+ +
+
+ + + + + + + + + + + + + + +
+ + + +
+ +
+
+
+
+ +
Here is some text
+
And some more
+
+ +
+
+
+ +
+
+
+
+ test +
+ +

+ 1 + 2 + 34 + 56 +

+ +

+ 1 + 2 + 34 + 56 +

+ +
+
+
+ +
test
+ +
    +
  • +
      +
    • one
    • +
    • two
    • +
    • three
    • +
    +
      +
    • +
    +
  • +
+ + + +
    12
+ +
    +
  • +
  • +
  • +
  • +
+ +
    +
  • +
  • +
  • +
  • +
+ +
+ + + + +
+
+ + + + + +
+ + + + + +
+
+ +
hi
+
hi
+ +
+
+ +
+ +
+ + + +
+ +
test
+ +
hihello
+ +
hihello
+ +
12
+ +
+
+
12
+
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+
12
+
+ +
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+ + + + + + + + + +
+ + + + diff --git a/lib/zeptojs/vendor/evidence.js b/lib/zeptojs/vendor/evidence.js new file mode 100644 index 0000000..7ced34e --- /dev/null +++ b/lib/zeptojs/vendor/evidence.js @@ -0,0 +1,1059 @@ +/* evidence.js, version 0.6 + * + * Copyright (c) 2009 Tobie Langel (http://tobielangel.com) + * + * evidence.js is freely distributable under the terms of an MIT-style license. + *--------------------------------------------------------------------------*/ + +(function(global) { + var originalEvidence = global.Evidence, + originalOnload = global.onload; + + function Evidence() { + TestCase.extend.apply(TestCase, arguments); + } + + function noConflict() { + global.Evidence = originalEvidence; + return Evidence; + } + + Evidence.noConflict = noConflict; + Evidence.VERSION = '0.6'; + +var FILE_REGEXP = /.*?\/(\w+\.html)(.*)/; + +function getNameFromFile() { + return (global.location || '').toString().replace(FILE_REGEXP, '$1'); +} + +function chain(subclass, superclass) { + function Subclass() {} + Subclass.prototype = superclass.prototype; + subclass.prototype = new Subclass(); + subclass.prototype.constructor = subclass; + return subclass; +} + +function defer(block, context) { + if ('setTimeout' in global) { + window.setTimeout(function() { + block.call(context); + }, 10); + } else { + block.call(context); + } +} +function AssertionSkippedError(message) { + this.message = message; +} + +AssertionSkippedError.displayName = 'AssertionSkippedError'; + +(function(p) { + p.name = 'AssertionSkippedError'; +})(AssertionSkippedError.prototype); +Evidence.AssertionSkippedError = AssertionSkippedError; +function AssertionFailedError(message, template, args) { + this.message = message; + this.template = template || ''; + this.args = args; +} + +AssertionFailedError.displayName = 'AssertionFailedError'; + +(function(p) { + p.name = 'AssertionFailedError'; +})(AssertionFailedError.prototype); +Evidence.AssertionFailedError = AssertionFailedError; +function AssertionMessage(message, template, args) { + this.message = message.replace(/%/g, '%%'); + this.template = template || ''; + this.args = args; +} + +AssertionMessage.displayName = 'AssertionMessage'; + +(function(p) { + function toString() { + return UI.printf(this.message + this.template, this.args); + } + p.toString = toString; +})(AssertionMessage.prototype); +Evidence.AssertionMessage = AssertionMessage; + +var Assertions = (function() { + function _assertExpression(expression, message, template) { + /*for (var i=0; i < 100000; i++) { + (function(){})() + }*/ + if (expression) { + this.addAssertion(); + } else { + var args = Array.prototype.slice.call(arguments, 3); + throw new AssertionFailedError(message, template, args); + } + } + + function skip(message) { + throw new AssertionSkippedError(message || 'Skipped!'); + } + + function fail(message) { + this._assertExpression(false, message || 'Flunked!'); + } + + function assert(test, message) { + this._assertExpression( + !!test, + message || 'Failed assertion.', + 'Expected %o to evaluate to true.', test + ); + } + + function refute(test, message) { + this._assertExpression( + !test, + message || 'Failed refutation.', + 'Expected %o to evaluate to false.', test + ); + } + + function assertTrue(test, message) { + this._assertExpression( + (test === true), + message || 'Failed assertion.', + 'Expected %o to be true.', test + ); + } + + function refuteTrue(test, message) { + this._assertExpression( + (test !== true), + message || 'Failed refutation.', + 'Expected %o to not be true.', test + ); + } + + function assertNull(test, message) { + this._assertExpression( + (test === null), + message || 'Failed assertion.', + 'Expected %o to be null.', test + ); + } + + function refuteNull(test, message) { + this._assertExpression( + (test !== null), + message || 'Failed refutation.', + 'Expected %o to not be null.', test + ); + } + + function assertUndefined(test, message) { + this._assertExpression( + (typeof test === 'undefined'), + message || 'Failed assertion.', + 'Expected %o to be undefined.', test + ); + } + + function refuteUndefined(test, message) { + this._assertExpression( + (typeof test !== 'undefined'), + message || 'Failed refutation.', + 'Expected %o to not be undefined.', test + ); + } + + function assertFalse(test, message) { + this._assertExpression( + (test === false), + message || 'Failed assertion.', + 'Expected %o to be false.', test + ); + } + + function refuteFalse(test, message) { + this._assertExpression( + (test !== false), + message || 'Failed refutation.', + 'Expected %o to not be false.', test + ); + } + + function assertEqual(expected, actual, message) { + this._assertExpression( + (expected == actual), + message || 'Failed assertion.', + 'Expected %o to be == to %o.', actual, expected + ); + } + + function refuteEqual(expected, actual, message) { + this._assertExpression( + (expected != actual), + message || 'Failed refutation.', + 'Expected %o to be != to %o.', actual, expected + ); + } + + function assertIdentical(expected, actual, message) { + this._assertExpression( + (expected === actual), + message || 'Failed assertion.', + 'Expected %o to be === to %o.', actual, expected + ); + } + + function refuteIdentical(expected, actual, message) { + this._assertExpression( + (expected !== actual), + message || 'Failed refutation.', + 'Expected %o to be !== to %o.', actual, expected + ); + } + + function assertIn(property, object, message) { + this._assertExpression( + (property in object), + message || 'Failed assertion.', + 'Expected "%s" to be a property of %o.', property, object + ); + } + + function refuteIn(property, object, message) { + this._assertExpression( + !(property in object), + message || 'Failed refutation.', + 'Expected "%s" to not be a property of %o.', property, object + ); + } + + return { + _assertExpression: _assertExpression, + skip: skip, + assert: assert, + refute: refute, + assertNot: refute, + assertTrue: assertTrue, + assertNull: assertNull, + assertUndefined: assertUndefined, + assertFalse: assertFalse, + assertIdentical: assertIdentical, + refuteIdentical: refuteIdentical, + assertEqual: assertEqual, + refuteEqual: refuteEqual, + assertIn: assertIn, + refuteIn: refuteIn, + fail: fail, + flunk: fail + }; +})(); + Evidence.Assertions = Assertions; +function TestCase(methodName) { + this._methodName = methodName; + this.name = methodName; +} + +(function() { + function extend(name, methods) { + function TestCaseSubclass(methodName) { + TestCase.call(this, methodName); + } + + if (!methods) { + methods = name; + name = getNameFromFile(); + } + + chain(TestCaseSubclass, this); + TestCaseSubclass.displayName = name; + TestCaseSubclass.extend = extend; + + for(var prop in methods) { + TestCaseSubclass.prototype[prop] = methods[prop]; + } + TestCase.subclasses.push(TestCaseSubclass); + return TestCaseSubclass; + } + + function AssertionsMixin() {} + AssertionsMixin.prototype = Assertions; + TestCase.prototype = new AssertionsMixin(); + TestCase.constructor = TestCase; + + TestCase.displayName = 'TestCase'; + TestCase.extend = extend; + TestCase.subclasses = []; + TestCase.defaultTimeout = 10000; +})(); + +(function(p) { + function run(result) { + if (result) { this._result = result; } + try { + if (this._nextAssertions) { + this._result.restartTest(this); + this._nextAssertions(this); + } else { + /*this._globalProperties = objectKeys(global);*/ + this._result.startTest(this); + this.setUp(this); + this[this._methodName](this); + } + } catch(e) { + this._filterException(e); + } finally { + if (this._paused) { + this._result.pauseTest(this); + } else { + try { + this.tearDown(this); + } catch(e) { + this._filterException(e); + } finally { + this._nextAssertions = null; + this._result.stopTest(this); + defer(function() { + this.parent.next(); + }, this); + } + } + } + } + + function _filterException(e) { + var name = e.name; + switch(name) { + case 'AssertionFailedError': + this._result.addFailure(this, e); + break; + case 'AssertionSkippedError': + this._result.addSkip(this, e); + break; + default: + this._result.addError(this, e + ('stack' in e ? "\n"+e.stack : '')); + } + } + + function pause(assertions) { + this._paused = true; + var self = this; + if (assertions) { this._nextAssertions = assertions; } + self._timeoutId = global.setTimeout(function() { + self.resume(function() { + self.fail('Test timed out. Testing was not resumed after being paused.'); + }); + }, TestCase.defaultTimeout); + } + + function resume(assertions) { + if (this._paused) { // avoid race conditions + this._paused = false; + global.clearTimeout(this._timeoutId); + if (assertions) { this._nextAssertions = assertions; } + this.run(); + } + } + + function size() { + return 1; + } + + function toString() { + return this.constructor.displayName + '#' + this.name; + } + + function addAssertion() { + this._result.addAssertion(); + } + + p.run = run; + p.addAssertion = addAssertion; + p._filterException = _filterException; + p.pause = pause; + p.resume = resume; + p.size = size; + p.toString = toString; + p.setUp = function() {}; + p.tearDown = function() {}; +})(TestCase.prototype); + Evidence.TestCase = TestCase; +function TestSuite(name, tests) { + this.name = name; + this._tests = []; + if (tests) { + this.push.apply(this, tests); + } +} + +TestSuite.displayName = 'TestSuite'; + +(function(p) { + function run(result) { + this._index = 0; + this._result = result; + result.startSuite(this); + this.next(); + return result; + } + + function next() { + var next = this._tests[this._index]; + if (next) { + this._index++; + next.run(this._result); + } else { + this._result.stopSuite(this); + if (this.parent) { + this.parent.next(); + } else { + this._result.stop(new Date()); + } + } + } + + function push() { + for (var i = 0, length = arguments.length; i < length; i++) { + var test = arguments[i]; + test.parent = this; + this._tests.push(test); + } + } + + function addTest(test) { + test.parent = this; + this._tests.push(test); + } + + function addTests(tests) { + for (var i = 0, length = tests.length; i < length; i++) { + this.addTest(tests[i]); + } + } + + function size() { + var tests = this._tests, + length = tests.length, + sum = 0; + + for (var i = 0; i < length; i++) { + sum += tests[i].size(); + } + return sum; + } + + function isEmpty() { + return this.size() === 0; + } + + function toString() { + return this.name; + } + p.run = run; + p.next = next; + p.push = push; + p.size = size; + p.isEmpty = isEmpty; + p.toString = toString; +})(TestSuite.prototype); + Evidence.TestSuite = TestSuite; +function TestRunner() { +} + +TestRunner.displayName = 'TestRunner'; + +(function(p) { + function run(suite) { + suite.parent = null; + var result = this._makeResult(); + result.start(new Date()); + suite.run(result); + return result; + } + + function _makeResult() { + return new TestResult(); + } + + p.run = run; + p._makeResult = _makeResult; +})(TestRunner.prototype); + Evidence.TestRunner = TestRunner; +function TestLoader() { +} + +TestLoader.displayName = 'TestLoader'; + +(function(p) { + function loadTestsFromTestCase(testcaseClass) { + var suite = new TestSuite(testcaseClass.displayName), + props = this.getTestCaseNames(testcaseClass); + for (var i=0; i < props.length; i++) { + suite.push(new testcaseClass(props[i])); + } + return suite; + } + + function loadTestsFromTestCases(testcases) { + var suite = new TestSuite(getNameFromFile()); + for (var i = 0; i < testcases.length; i++) { + var testcase = testcases[i]; + var subSuite = defaultLoader.loadTestsFromTestCase(testcase); + if (!subSuite.isEmpty()) { suite.push(subSuite); } + } + return suite; + } + + function getTestCaseNames(testcaseClass) { + var results = [], + proto = testcaseClass.prototype, + prefix = this.testMethodPrefix; + + for (var property in proto) { + if (property.indexOf(prefix) === 0) { + results.push(property); + } + } + return results.sort(); + } + + function loadRegisteredTestCases() { + return loadTestsFromTestCases(TestCase.subclasses); + } + + p.loadTestsFromTestCase = loadTestsFromTestCase; + p.loadRegisteredTestCases = loadRegisteredTestCases; + p.loadTestsFromTestCases = loadTestsFromTestCases; + p.testMethodPrefix = 'test'; + p.getTestCaseNames = getTestCaseNames; + +})(TestLoader.prototype); + Evidence.TestLoader = TestLoader; +function AutoRunner() { + if (global.console && global.console.log) { + this.logger = Logger; + } else if (Object.prototype.toString.call(global.environment) === '[object Environment]' && global.print) { + this.logger = CommandLineLogger; + } else { + this.logger = PopupLogger; + } + this.autoRun = true; + this.verbosity = Logger.INFO; + this.runner = ConsoleTestRunner; +} + +(function() { + function run(options) { + var autoRunner = new this(); + options = options || autoRunner.retrieveOptions(); + autoRunner.processOptions(options); + if (autoRunner.autoRun) { autoRunner.run() }; + } + + AutoRunner.run = run; + AutoRunner.displayName = 'AutoRunner'; + AutoRunner.LOGGERS = { + console: Logger, + popup: PopupLogger, + command_line: CommandLineLogger + }; + + AutoRunner.RUNNERS = { + console: ConsoleTestRunner + }; +})(); + +(function(p) { + function run() { + var logger = new this.logger(this.verbosity), + runner = new this.runner(logger), + suite = defaultLoader.loadRegisteredTestCases(); + if (suite._tests.length <= 1) { + suite = suite._tests[0]; + } + return runner.run(suite); + } + + function processQueryString(str) { + var results = {}; + str = (str + '').match(/^(?:[^?#]*\?)([^#]+?)(?:#.*)?$/); + str = str && str[1]; + + if (!str) { return results; } + + var pairs = str.split('&'), + length = pairs.length; + if (!length) { return results; } + + for (var i = 0; i < length; i++) { + var pair = pairs[i].split('='), + key = decodeURIComponent(pair[0]), + value = pair[1]; + value = value ? decodeURIComponent(value) : true; + results[key] = value; + } + return results; + } + + function processArguments(args) { // RHINO + var results = {}; + + for (var i = 0; i < args.length; i++) { + var arg = args[i]; + if (arg.indexOf('-') === 0) { + var value = args[i + 1]; + if (value && value.indexOf('-') !== 0) { + i++; + } else { + value = true; + } + results[arg.substr(1)] = value; + } + } + return results; + } + + function retrieveOptions() { + if (global.location) { + return this.processQueryString(global.location); + } + if (global.arguments) { + return this.processArguments(global.arguments); + } + return {}; + } + + function processOptions(options) { + for(var key in options) { + var value = options[key]; + switch(key) { + case 'timeout': + TestCase.defaultTimeout = global.parseFloat(value) * 1000; + break; + case 'run': + this.autoRun = value === 'false' ? false : true; + break; + case 'logger': + this.logger = AutoRunner.LOGGERS[value]; + break; + case 'verbosity': + var i = global.parseInt(value); + this.verbosity = global.isNaN(i) ? Logger[value] : i; + break; + case 'runner': + this.runner = AutoRunner.RUNNERS[value]; + break; + } + } + } + + p.run = run; + p.processQueryString = processQueryString; + p.processArguments = processArguments; + p.retrieveOptions = retrieveOptions; + p.processOptions = processOptions; +})(AutoRunner.prototype); + Evidence.AutoRunner = AutoRunner; +function TestResult() { + this.testCount = 0; + this.assertionCount = 0; + this.skipCount = 0; + this.skips = []; + this.failureCount = 0; + this.failures = []; + this.errors = []; + this.errorCount = 0; + this.testCount = 0; +} + +TestResult.displayName = 'TestResult'; + +(function(p) { + function addAssertion() { + this.assertionCount++; + } + + function addSkip(testcase, reason) { + this.skipCount++; + this.skips.push(reason); + } + + function addFailure(testcase, reason) { + this.failureCount++; + this.failures.push(reason); + } + + function addError(testcase, error) { + this.errorCount++; + this.errors.push(error); + } + + function startTest(testcase) { + this.testCount++; + } + + function stopTest(testcase) {} + + function pauseTest(testcase) {} + + function restartTest(testcase) {} + + function startSuite(suite) {} + + function stopSuite(suite) {} + + function start(t0) { + this.t0 = t0; + } + + function stop(t1) { + this.t1 = t1; + } + + function toString() { + return this.testCount + ' tests, ' + + this.assertionCount + ' assertions, ' + + this.failureCount + ' failures, ' + + this.errorCount + ' errors, ' + + this.skipCount + ' skips'; + } + + p.addAssertion = addAssertion; + p.addSkip = addSkip; + p.addFailure = addFailure; + p.addError = addError; + p.startTest = startTest; + p.stopTest = stopTest; + p.pauseTest = pauseTest; + p.restartTest = restartTest; + p.startSuite = startSuite; + p.stopSuite = stopSuite; + p.start = start; + p.stop = stop; + p.toString = toString; +})(TestResult.prototype); + Evidence.TestResult = TestResult; +var Console = {}; + +function Logger(level) { + if (typeof level !== 'undefined') { + this.level = level; + } +} + +Logger.displayName = 'Logger'; +Logger.LEVELS = ['NOTSET', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL']; +Logger.CRITICAL = 5; +Logger.ERROR = 4; +Logger.WARN = 3; +Logger.INFO = 2; +Logger.DEBUG = 1; +Logger.NOTSET = 0; + +(function(p) { + function critical(template, params) { + this.log(Logger.CRITICAL, template, params); + } + + function error(template, params) { + this.log(Logger.ERROR, template, params); + } + + function warn(template, params) { + this.log(Logger.WARN, template, params); + } + + function info(template, params) { + this.log(Logger.INFO, template, params); + } + + function debug(template, params) { + this.log(Logger.DEBUG, template, params); + } + + function log(level, template, params) { + level = level || Logger.NOTSET; + var c = global.console; + + var method = Logger.LEVELS[level].toLowerCase(); + if (method === 'critical') { method = 'error'; } + method = (method in c) ? method : 'log'; + + if (level >= this.level) { + if (params) { + params = params.slice(0); + params.unshift(template); + c[method].apply(c, params); + } else { + c[method](template); + } + } + } + + p.log = log; + p.critical = critical; + p.error = error; + p.warn = warn; + p.info = info; + p.debug = debug; + p.level = 0; +})(Logger.prototype); +Console.Logger = Logger; +function PopupLogger(level) { + Logger.call(this, level); +} + +chain(PopupLogger, Logger); +PopupLogger.displayName = 'PopupLogger'; + +(function(p) { + var BASIC_STYLES = 'color: #333; background-color: #fff; font-family: monospace; border-bottom: 1px solid #ccc;'; + var STYLES = { + WARN: 'color: #000; background-color: #fc6;', + ERROR: 'color: #f00; background-color: #fcc;', + CRITICAL: 'color: #fff; background-color: #000;' + }; + + function _cleanup(html) { + return html.replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&').replace(/[\n\r]+/, '
'); + } + + function _makePopup() { + var popup = global.open('','popup','height=400,width=400'); + var doc = popup.document; + doc.write('\ + \ + \ + \ + Console\ + \ +
\ + '); + doc.close(); + popup.focus(); + return popup; + } + + function _appendLine(level, msg) { + this.popup = this.popup || this._makePopup(); + var levelName = Logger.LEVELS[level]; + + var html = '
'; + if (level > Logger.INFO) { + html += ''; + html += levelName; + html += ': '; + } + html += _cleanup(msg); + html += '
'; + var doc = this.popup.document, + div = doc.createElement('div'); + div.innerHTML = html; + html = div.firstChild; + div = null; + doc.getElementById('evidence_console').appendChild(html); + } + + function log(level, msg, params) { + level = level || Logger.NOTSET; + if (level >= this.level) { + if (params) { + msg = UI.printf(msg, params); + } + this._appendLine(level, msg); + } + } + + p.log = log; + p._makePopup = _makePopup; + p._appendLine = _appendLine; +})(PopupLogger.prototype); +Console.PopupLogger = PopupLogger; +function CommandLineLogger(level) { + Logger.call(this, level); +} + +chain(CommandLineLogger, Logger); +CommandLineLogger.displayName = 'CommandLineLogger'; + +(function(p) { + + function log(level, msg, params) { + level = level || Logger.NOTSET; + if (level >= this.level) { + var prefix = ''; + if (level > Logger.INFO) { + prefix = Logger.LEVELS[level]+ ': '; + } + if (params) { + msg = UI.printf(msg, params); + } + global.print(prefix + msg); + } + } + + p.log = log; +})(CommandLineLogger.prototype); +Console.CommandLineLogger = CommandLineLogger; +function ConsoleTestRunner(logger) { + TestRunner.call(this); + this.logger = logger; +} + +chain(ConsoleTestRunner, TestRunner); +ConsoleTestRunner.displayName = 'ConsoleTestRunner'; + +(function(p) { + function _makeResult() { + return new ConsoleTestResult(this.logger); + } + + p._makeResult = _makeResult; +})(ConsoleTestRunner.prototype); +Console.TestRunner = ConsoleTestRunner; +function ConsoleTestResult(logger) { + TestResult.call(this); + this.logger = logger; +} + +chain(ConsoleTestResult, TestResult); +ConsoleTestResult.displayName = 'ConsoleTestResult'; + +(function(p) { + var _super = TestResult.prototype; + + function addAssertion() { + this.assertionCount++; + } + + function addSkip(testcase, msg) { + _super.addSkip.call(this, testcase, msg); + this.logger.warn('Skipping testcase ' + testcase + ': ' + msg.message); + } + + function addFailure(testcase, msg) { + _super.addFailure.call(this, testcase, msg); + this.logger.error(testcase + ': ' + msg.message + ' ' + msg.template, msg.args); + } + + function addError(testcase, error) { + _super.addError.call(this, testcase, error); + this.logger.error(testcase + ' threw an error. ' + error); + } + + function startTest(testcase) { + _super.startTest.call(this, testcase); + this.logger.debug('Started testcase ' + testcase + '.'); + } + + function stopTest(testcase) { + this.logger.debug('Completed testcase ' + testcase + '.'); + } + + function pauseTest(testcase) { + this.logger.info('Paused testcase ' + testcase + '.'); + } + + function restartTest(testcase) { + this.logger.info('Restarted testcase ' + testcase + '.'); + } + + function startSuite(suite) { + this.logger.info('Started suite ' + suite + '.'); + } + + function stopSuite(suite) { + this.logger.info('Completed suite ' + suite + '.'); + } + + function start(t0) { + _super.start.call(this, t0); + this.logger.info('Started tests.'); + } + + function stop(t1) { + _super.stop.call(this, t1); + this.logger.info('Completed tests in ' + ((t1 - this.t0)/1000) + 's.'); + this.logger.info(this.toString() + '.'); + } + + p.addAssertion = addAssertion; + p.addSkip = addSkip; + p.addFailure = addFailure; + p.addError = addError; + p.startTest = startTest; + p.stopTest = stopTest; + p.pauseTest = pauseTest; + p.restartTest = restartTest; + p.startSuite = startSuite; + p.stopSuite = stopSuite; + p.start = start; + p.stop = stop; +})(ConsoleTestResult.prototype); + + +Console.TestResult = ConsoleTestResult; +var UI = (function() { + function printf(template, args, inspector) { + var parts = [], m, + regexp = /(^%|.%)([a-zA-Z])/, + args = args.splice(0); // clone args + + inspector = inspector || String; + + if (template.length <= 0) { + return ''; + } + while (m = regexp.exec(template)) { + var match = m[0], index = m.index, type, arg; + + if (match.indexOf('%%') === 0) { + parts.push(template.substr(0, index)); + parts.push(match.substr(1)); + } else { + parts.push(template.substr(0, match.indexOf('%' === 0) ? index + 1 : index)); + type = m[2]; + arg = args.shift(); + arg = inspector(arg, type); + parts.push(arg); + } + template = template.substr(index + match.length); + } + parts.push(template); + return parts.join(''); + } + + return { + printf: printf, + Console: Console + }; +})(); + Evidence.UI = UI; + + var defaultLoader = new TestLoader(); + Evidence.defaultLoader = defaultLoader; + + global.Evidence = Evidence; + + if (global.location) { + global.onload = function() { + if (typeof originalOnload === 'function') { + originalOnload.call(global); + } + AutoRunner.run(); + }; + } else if (global.arguments) { + var runtime = java.lang.Runtime.getRuntime(); + var thread = new java.lang.Thread(function() { + AutoRunner.run(); + }); + runtime.addShutdownHook(thread); + } + +})(this); diff --git a/package.json b/package.json index e0d2737..05550aa 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,26 @@ { "name": "bellows", - "version": "0.3.4", - "description": "A responsive, mobile first accordion UI module", + "version": "2.0.0", + "description": "A mobile first accordion UI plugin", "devDependencies": { "grunt": "~0.4.x", "grunt-contrib-uglify": "~0.1.1", - "grunt-s3": "~0.1.0", "grunt-css": "~0.5.4", "grunt-shell": "~0.2.1", - "grunt-release": "0.7.0", - "grunt-zip": "~0.4.2", - "grunt-clean": "~0.4.0", + "grunt-contrib-clean": "~0.5.0", "grunt-contrib-connect": "~0.3.0", "grunt-contrib-watch": "~0.5.1", - "grunt-contrib-copy": "~0.4.1" + "grunt-contrib-copy": "~0.4.1", + "grunt-autoprefixer": "~0.8.0", + "grunt-contrib-sass": "~0.7.3", + "grunt-contrib-jshint": "0.10.0", + "jshint": "2.5.0", + "grunt-jscs-checker": "0.4.1", + "mobify-code-style": "0.0.3", + "grunt-mocha-phantomjs": "~0.3.2", + "grunt-open": "^0.2.3", + "mocha": "~1.14.0", + "chai": "~1.9.0" }, "license": "MIT" } diff --git a/src/bellows-style.css b/src/bellows-style.css deleted file mode 100644 index 45c3c12..0000000 --- a/src/bellows-style.css +++ /dev/null @@ -1,183 +0,0 @@ -/* Base Style */ -.m-bello { - color: black; -} -.m-bellows .m-header { - height: 54px; - line-height: 52px; - padding: 0 20px; - border: solid 1px #ededed; - border-width: 1px 0; - position: relative; - cursor: pointer; - z-index: 5; - background: #ffffff; /* Old browsers */ - background: -moz-linear-gradient(top, #ffffff 0%, #f6f6f6 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(100%,#f6f6f6)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #ffffff 0%,#f6f6f6 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #ffffff 0%,#f6f6f6 100%); /* Opera 11.10+ */ - background: -ms-linear-gradient(top, #ffffff 0%,#f6f6f6 100%); /* IE10+ */ - background: linear-gradient(to bottom, #ffffff 0%,#f6f6f6 100%); /* W3C */ -} - .m-bellows .m-header:hover, .m-bellows .hm-eader:focus { - z-index: 2; - background: #ffffff; /* Old browsers */ - background: -moz-linear-gradient(top, #ffffff 0%, #f6f6f6 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(100%,#f6f6f6)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #f6f6f6 0%,#e6e6e6 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #ffffff 0%,#f6f6f6 100%); /* Opera 11.10+ */ - background: -ms-linear-gradient(top, #ffffff 0%,#f6f6f6 100%); /* IE10+ */ - background: linear-gradient(to bottom, #f6f6f6 0%,#f0f0f0 100%); /* W3C */ - border-bottom-color: #d6d6d6; - } - .m-bellows .m-item:first-child .m-header { - -webkit-border-top-left-radius: 9px; - -webkit-border-top-right-radius: 9px; - -moz-border-radius-topleft: 9px; - -moz-border-radius-topright: 9px; - border-top-left-radius: 9px; - border-top-right-radius: 9px; - } - .m-bellows .m-item:last-child .m-header, .m-bellows .m-active:last-child .m-inner-content { - -webkit-border-bottom-left-radius: 9px; - -webkit-border-bottom-right-radius: 9px; - -moz-border-radius-bottomleft: 9px; - -moz-border-radius-bottomright: 9px; - border-bottom-left-radius: 9px; - border-bottom-right-radius: 9px; - } - .m-bellows .m-active:last-child .m-header { - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; - } -.m-bellows .m-header a { - color: black; -} -.m-bellows .m-inner-content { - padding: 10px 20px; - background: white; -} -.m-bellows h2 { - margin: 0; - color: black; - text-shadow: none; -} - - -/* Accordion with Images */ -.m-indicators-images .m-header:after { - content : ""; - display: block; - position: absolute; - top: 50%; - right: 10px; - margin-top: -11px; - z-index: 4; - width: 22px; - height: 22px; - background: url(../img/bellows.png) 0 -20px no-repeat; -} - .m-indicators-images .m-active .m-header:after { - background-position: 0 -43px; - } -/* -- Retina version -- */ -@media only screen and (-moz-min-device-pixel-ratio: 1.5), only screen and (-o-min-device-pixel-ratio: 3/2), only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-devicepixel-ratio: 1.5), only screen and (min-resolution: 1.5dppx) { - .m-indicators-images .m-header:after { - background-position: -11px 0; - background-size: 33px 43px; - } - .m-indicators-images .m-active .m-header:after { - background-position: -11px -22px; - } -} - - - -/* Accordion with CSS arrow indicators */ -.m-arrows .m-active .m-header:after { - top: 100%; - border: solid transparent; - content: " "; - height: 0; - width: 0; - position: absolute; - pointer-events: none; - border-top-color: #f6f6f6; - border-width: 20px; - left: 50%; - margin-left: -20px; - z-index: 9; -} - .m-arrows .m-active .m-header:hover:after { - border-top-color: #e6e6e6; - } - -/* Accordion with CSS indicators */ -.m-indicators-css .m-header:after { - content : "+"; - color: #999; - text-shadow: #fff 0 1px 0; - display: block; - position: absolute; - top: 50%; - right: 15px; - margin-top: -13px; - z-index: 9; - font-size: 26px; - line-height: 22px; - font-weight: bold; -} - .m-indicators-css .m-active .m-header:after { - content: "\2212"; /* Unicode minus sign */ - } - - - -/* Accordion with advanced CSS indicators */ -.m-css-advanced .m-header:after { - color: rgba(255,255,255,0.9); - font-weight: normal; - text-align: center; - text-shadow: none; - background: #c5c5c5; /* Old browsers */ - background: -moz-linear-gradient(top, #c5c5c5 0%, #acacac 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#c5c5c5), color-stop(100%,#acacac)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #c5c5c5 0%,#acacac 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #c5c5c5 0%,#acacac 100%); /* Opera 11.10+ */ - background: -ms-linear-gradient(top, #c5c5c5 0%,#acacac 100%); /* IE10+ */ - background: linear-gradient(to bottom, #c5c5c5 0%,#acacac 100%); /* W3C */ - font-size: 22px; - line-height: 16px; - width: 20px; - height: 20px; - margin-top: -10px; - -webkit-border-radius: 25px; - -moz-border-radius: 25px; - border-radius: 25px; - -webkit-box-shadow: #fff 0 1px 0; - -moz-box-shadow: #fff 0 1px 0; - box-shadow: #fff 0 1px 0; -} -.m-css-advanced .m-header:hover:after { - color: #fff; - background: #b4b4b4; /* Old browsers */ - background: -moz-linear-gradient(top, #b4b4b4 0%, #a3a3a3 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b4b4b4), color-stop(100%,#a3a3a3)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #b4b4b4 0%,#a3a3a3 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #b4b4b4 0%,#a3a3a3 100%); /* Opera 11.10+ */ - background: -ms-linear-gradient(top, #b4b4b4 0%,#a3a3a3 100%); /* IE10+ */ - background: linear-gradient(to bottom, #b4b4b4 0%,#a3a3a3 100%); /* W3C */ -} -.m-css-advanced .m-active .m-header:after { - background: #8a8a8a; /* Old browsers */ - background: -moz-linear-gradient(top, #8a8a8a 0%, #a3a3a3 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#8a8a8a), color-stop(100%,#a3a3a3)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #8a8a8a 0%,#a3a3a3 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #8a8a8a 0%,#a3a3a3 100%); /* Opera 11.10+ */ - background: -ms-linear-gradient(top, #8a8a8a 0%,#a3a3a3 100%); /* IE10+ */ - background: linear-gradient(to bottom, #8a8a8a 0%,#a3a3a3 100%); /* W3C */ - -webkit-box-shadow: #fff 0 -1px 0; - -moz-box-shadow: #fff 0 -1px 0; - box-shadow: #fff 0 -1px 0; -} \ No newline at end of file diff --git a/src/bellows.css b/src/bellows.css deleted file mode 100644 index 16b22ab..0000000 --- a/src/bellows.css +++ /dev/null @@ -1,33 +0,0 @@ -.m-bellows { - list-style: none; -} -.m-bellows li { - list-style: none; - margin: 0; -} -.m-bellows .m-content { - -webkit-transition: max-height 0.5s ease; - -moz-transition: max-height 0.5s ease; - -o-transition: max-height 0.5s ease; - transition: max-height 0.5s ease; - overflow: hidden; - max-height: 0; -} -.m-bellows .m-active > .m-content { - max-height: 10000px; /* IE hack */ -} - -.m-bellows .m-header { - z-index: 1; - position: relative; - margin-top: -1px; - pointer-events: cursor; /* For iOS */ -} - -.m-bellows h3 { - margin: 0; -} - -.m-bellows .m-header > a { - pointer-events: none; -} diff --git a/src/bellows.js b/src/bellows.js deleted file mode 100644 index a6afef2..0000000 --- a/src/bellows.js +++ /dev/null @@ -1,310 +0,0 @@ -var Mobify = window.Mobify = window.Mobify || {}; -Mobify.$ = Mobify.$ || window.Zepto || window.jQuery; -Mobify.UI = Mobify.UI || {}; - -(function($, document) { - $.support = $.support || {}; - - $.extend($.support, { - 'touch': 'ontouchend' in document - }); - -})(Mobify.$, document); - - -/** - @module Holds common functions relating to UI. -*/ -Mobify.UI.Utils = (function($) { - var exports = {} - , has = $.support; - /** - Events (either touch or mouse) - */ - exports.events = (has.touch) - ? {down: 'touchstart', move: 'touchmove', up: 'touchend'} - : {down: 'mousedown', move: 'mousemove', up: 'mouseup'}; - - /** - Returns the position of a mouse or touch event in (x, y) - @function - @param {Event} touch or mouse event - @returns {Object} X and Y coordinates - */ - exports.getCursorPosition = (has.touch) - ? function(e) {e = e.originalEvent || e; return {x: e.touches[0].clientX, y: e.touches[0].clientY}} - : function(e) {return {x: e.clientX, y: e.clientY}}; - - - /** - Returns prefix property for current browser. - @param {String} CSS Property Name - @return {String} Detected CSS Property Name - */ - exports.getProperty = function(name) { - var prefixes = ['Webkit', 'Moz', 'O', 'ms', ''] - , testStyle = document.createElement('div').style; - - for (var i = 0; i < prefixes.length; ++i) { - if (testStyle[prefixes[i] + name] !== undefined) { - return prefixes[i] + name; - } - } - - // Not Supported - return; - }; - - // determine which transition event to use - function whichTransitionEvent(){ - // http://stackoverflow.com/questions/5023514/how-do-i-normalize-css3-transition-functions-across-browsers - // hack for ios 3.1.* because of poor transition support. - if (/iPhone\ OS\ 3_1/.test(navigator.userAgent)) { - return undefined; - } - - var el = document.createElement('fakeelement'); - var transitions = { - 'transition':'transitionEnd transitionend', - 'OTransition':'oTransitionEnd', - 'MSTransition':'msTransitionEnd', - 'MozTransition':'transitionend', - 'WebkitTransition':'webkitTransitionEnd' - }; - - var t; - for(t in transitions){ - if( el.style[t] !== undefined ){ - return transitions[t]; - } - } - return; - }; - - $.extend(exports.events, { - 'transitionend': whichTransitionEvent() - }); - - return exports; - -})(Mobify.$); - - -/* - Supports accordions in an accordion -*/ -Mobify.UI.Bellows = (function($, Utils) { - - var has = $.support; - - // Constructor - var Bellows = function(element, options) { - - // Bellows settings - this.settings = $.extend({ - dragRadius: 10 - , closedClass: 'm-closed' - , openedClass: 'm-opened' - , activeClass: 'm-active' - , contentClass: 'm-content' - , innerContentClass: 'm-inner-content' - , headerClass: 'm-header' - , itemClass: 'm-item' - , moduleClass: 'm-bellows' - , onTransitionDone: null - , onOpened: null - , onClosed: null - }, options || {}); - - this.$element = $(element); - - $.extend(this, this.bind()); - return this; - }; - - Bellows.prototype.bind = function() { - var $element = this.$element - , xy - , dxy - , settings = this.settings - , dragRadius = settings.dragRadius - , openedClass = settings.openedClass - , closedClass = settings.closedClass - , activeClass = settings.activeClass - , contentClass = settings.contentClass - , headerClass = settings.headerClass - , itemClass = settings.itemClass - , moduleClass = settings.moduleClass; - - function endTransition(){ - // transition attached to .content elements, use parent to grab .item - var $item = $(this).parent(); - // if the transition is ending - if ($item.hasClass(closedClass)) $(this).parent().removeClass(activeClass); - // Execute any callbacks that were passed - executeCallbacks($item.hasClass(closedClass) ? 'closing' : 'opening'); - recalculateHeight($element); - }; - - // Recalculate proper height for specified bellows - function recalculateHeight($bellows) { - var height = 0; - - $bellows.children( '.' + itemClass).each(function(index) { - var $item = $(this); - height += $item.height(); - }); - - $bellows.css('min-height', height + 'px'); - } - - // Calculate height of individual accordion item (useful for dynamic item creation) - function recalculateItemHeight($item) { - - var $content = $item.children('.' + contentClass); - // determine which height function to use (outerHeight not supported by zepto) - var contentChildren = $content.children(); - var contentHeight = ('outerHeight' in contentChildren) ? contentChildren['outerHeight']() : contentChildren['height'](); - $content.css('max-height', contentHeight * 1.5 +'px'); - // if transitions are supported, minimize browser reflow by adding the height - // of the to-be expanded content element to the height of the entire accordion - if (Utils.events.transitionend) { - $element.css('min-height', $element.height() + 'px'); - } - - // we need to wait to recalculate, so that the heights are calculated properly first - setTimeout(function() { - $currentBellows = $item.closest('.' + moduleClass); - recalculateHeight($currentBellows); - - // Resize the parent bellows if it exists - $parentBellows = $currentBellows.parent().closest('.' + moduleClass); - if($parentBellows.length > 0) { - recalculateItemHeight( $item.parent().closest('.' + itemClass)); - } - }, 150); - } - - // Execute any callback functions that are passed to open/close - function executeCallbacks(type) { - if(type === 'opening' && typeof settings['onOpened'] === "function") { - settings['onOpened'].apply(this, arguments); - } - if(type === 'closing' && typeof settings['onClosed'] === "function") { - settings['onClosed'].apply(this, arguments); - } - if(typeof settings['onTransitionDone'] === "function") { - settings['onTransitionDone'].apply(this, arguments); - } - } - - function close($item) { - if($item.hasClass(closedClass)) { executeCallbacks('closing'); } - // toggle opened and closed classes - $item.removeClass(openedClass); - $item.addClass(closedClass); - // toggle active class on close only if there is no transition support - if(!Utils.events.transitionend) $item.removeClass(activeClass); - // set max-height to 0 upon close - $item.children('.' + contentClass).css('max-height', '0px'); - } - - function open($item) { - if($item.hasClass(openedClass)) { executeCallbacks('opening'); } - $item.addClass(activeClass); - $item.removeClass(closedClass); - $item.addClass(openedClass); - recalculateItemHeight($item); - } - - function down(e) { - // get initial position on mouse/touch start - xy = Utils.getCursorPosition(e); - } - - function move(e) { - // update position upon move - dxy = Utils.getCursorPosition(e); - } - - function up(e) { - // if there is dragging, do not close/open bellows - if (dxy) { - dx = xy.x - dxy.x; - dy = xy.y - dxy.y; - dxy = undefined; - if ((dx*dx) + (dy*dy) > dragRadius*dragRadius) return; - } - // close or open item depending on active class - var $item = $(this).parent(); - if ($item.hasClass(activeClass)) { - close($item); - } - else { - open($item); - } - } - - function click(e) { - e.preventDefault(); - } - - - // Auto-open items that are hash linked or have openedClass class - var hash = location.hash; - var $hashitem = $element.find('.' + headerClass + ' a[href="'+hash+'"]'); - - if ($hashitem.length) { - open($hashitem.parent()); - } else if ($element.find('.' + openedClass).length) { - open($element.find('.' + openedClass)); - } - - var $headers = $element.children('.' + itemClass).children('.' + headerClass); - - $headers - .on(Utils.events.down, down) - .on(Utils.events.move, move) - .on(Utils.events.up, up) - .on('click', click); - if (Utils.events.transitionend) { - $element.on(Utils.events.transitionend, '.' + contentClass, endTransition); - } - - // API calls - return { - 'settings': settings - , 'open': open - , 'close': close - , 'recalculateItemHeight': recalculateItemHeight - }; - - }; - - Bellows.prototype.unbind = function() { - this.$element.off(); - }; - - Bellows.prototype.destroy = function() { - this.unbind(); - this.$element.remove(); - }; - - return Bellows; - -})(Mobify.$, Mobify.UI.Utils); - -(function($) { - $.fn.bellows = function(options) { - return this.each(function (i, elem) { - var $this = $(this) - , bellows = this.bellows; - - if (!bellows) { - bellows = new Mobify.UI.Bellows(this, options); // pass through options - } - - this.bellows = bellows; // Provide the bellows object to callers - }); - }; -})(Mobify.$); diff --git a/src/js/bellows.js b/src/js/bellows.js new file mode 100644 index 0000000..7fe7057 --- /dev/null +++ b/src/js/bellows.js @@ -0,0 +1,236 @@ +/* + Bellows.js v2.0.0 + */ +(function (factory) { + if (typeof define === 'function' && define.amd) { + /* + In AMD environments, you will need to define an alias + to your selector engine. i.e. either zepto or jQuery. + */ + define([ + '$', + 'velocity' + ], factory); + } else { + /* + Browser globals + */ + var framework = window.Zepto || window.jQuery; + factory(framework, framework.Velocity); + } +}(function($, Velocity) { + var PLUGIN_NAME = 'bellows'; + var noop = function() {}; + + var ITEM_CLASS = 'bellows__item'; + var OPENED_CLASS = 'bellows--is-open'; + var OPENING_CLASS = 'bellows--is-opening'; + var CLOSING_CLASS = 'bellows--is-closing'; + + var selectors = { + ITEM_HEADER: '> .bellows__item > .bellows__header', + ITEM_CONTENT_WRAPPER: '> .bellows__content-wrapper', + ITEM_CONTENT: '> .bellows__item > .bellows__content' + }; + + function Bellows(element, options) { + this._init(element, options); + } + + Bellows.VERSION = '2.0.0'; + + Bellows.DEFAULTS = { + singleItemOpen: false, + event: 'click', + duration: 200, + easing: 'swing', + open: noop, + opened: noop, + close: noop, + closed: noop + }; + + Bellows.prototype._init = function(element, options) { + this.options = $.extend(true, {}, Bellows.DEFAULTS, options); + + this.$bellows = $(element); + + this.$bellows + .find(selectors.ITEM_CONTENT) + // wrap content section of each item to facilitate padding + .wrap('
') + // add aria-hidden attribute to all hidden content wrappers + .parents('.bellows__item:not(.bellows--is-open)') + .find('.bellows__content-wrapper') + .attr('aria-hidden', true); + + this._bindEvents(); + }; + + Bellows.prototype._bindEvents = function() { + var plugin = this; + + // We use tappy here to eliminate the 300ms delay on clicking elements + this.$bellows + .find(selectors.ITEM_HEADER) + .bind(this.options.event, function(e) { + e.preventDefault(); + + plugin.toggle($(this).parent()); + }); + }; + + /* + Gets an element's height using Velocity's built-in property cache. + Used for getting heights before animations, for animating into an + element's space. + */ + Bellows.prototype._getHeight = function($element) { + return parseFloat(Velocity.CSS.getPropertyValue($element[0], 'height')); + }; + + /* + Sets the height of bellows so we animate into + the space rather than re-flowing the entire document + */ + Bellows.prototype._setHeight = function(height) { + this.$bellows.css('height', height || ''); + }; + + /* + Allow items to be found using an index + */ + Bellows.prototype._item = function(item) { + if (typeof item === 'number') { + item = this.$bellows.find('.' + ITEM_CLASS).eq(item); + } + + return item; + }; + + Bellows.prototype._trigger = function(eventName, data) { + eventName in this.options && this.options[eventName].call(this, $.Event(PLUGIN_NAME + ':' + eventName, { bubbles: false }), data); + }; + + Bellows.prototype.toggle = function($item) { + $item = this._item($item); + + this[$item.hasClass(OPENED_CLASS) ? 'close' : 'open']($item); + }; + + Bellows.prototype.open = function($item) { + $item = this._item($item); + + if ($item.hasClass(OPENED_CLASS)) { + return; + } + + var plugin = this; + var $contentWrapper = $item.find(selectors.ITEM_CONTENT_WRAPPER); + + if (this.options.singleItemOpen) { + this.closeAll(); + } + + this._trigger('open', { item: $item }); + + Velocity + .animate($contentWrapper, 'slideDown', { + begin: function() { + plugin._setHeight(plugin._getHeight(plugin.$bellows) + plugin._getHeight($contentWrapper)); + $item.addClass(OPENING_CLASS); + }, + duration: this.options.duration, + easing: this.options.easing, + complete: function() { + $item + .removeClass(OPENING_CLASS) + .addClass(OPENED_CLASS) + .attr('aria-hidden', true); + + plugin._setHeight(); + + plugin._trigger('opened', { item: $item }); + } + }); + }; + + Bellows.prototype.close = function($item) { + $item = this._item($item); + + if (!$item.hasClass(OPENED_CLASS)) { + return; + } + + var plugin = this; + var $contentWrapper = $item.find(selectors.ITEM_CONTENT_WRAPPER); + + this._trigger('close', { item: $item }); + + Velocity + .animate($contentWrapper, 'slideUp', { + begin: function() { + plugin._setHeight(plugin._getHeight(plugin.$bellows)); + $item + .removeClass(OPENED_CLASS) + .addClass(CLOSING_CLASS); + }, + duration: this.options.duration, + easing: this.options.easing, + complete: function() { + $item + .removeClass(CLOSING_CLASS) + .removeAttr('aria-hidden'); + + plugin._setHeight(); + + plugin._trigger('closed', { item: $item }); + } + }); + }; + + Bellows.prototype.closeAll = function() { + var plugin = this; + + this.$bellows.find('.' + OPENED_CLASS).each(function() { + plugin.close($(this)); + }); + }; + + /* + Bellows plugin definition + */ + $.fn.bellows = function(option) { + var args = Array.prototype.slice.call(arguments); + + return this.each(function() { + var $this = $(this); + var bellows = $this.data(PLUGIN_NAME); + var isMethodCall = typeof option === 'string'; + + // If bellows isn't initialized, we lazy-load initialize it. If it's + // already initialized, we can safely ignore the call. + if (!bellows) { + if (isMethodCall) { + throw 'cannot call methods on bellows prior to initialization; attempted to call method "' + option + '"'; + } + $this.data(PLUGIN_NAME, (bellows = new Bellows(this, option))); + } + + // invoke a public method on bellows, and skip private methods + if (isMethodCall) { + if (option.charAt(0) === '_' || typeof bellows[option] !== 'function') { + throw 'no such method "' + option + '" for bellows'; + } + + bellows[option].apply(bellows, args.length > 1 ? args.slice(1) : null); + } + }); + }; + + $.fn.bellows.Constructor = Bellows; + + $('[data-bellows]').bellows(); + + return $; +})); diff --git a/src/style/bellows-theme.scss b/src/style/bellows-theme.scss new file mode 100644 index 0000000..d99e03e --- /dev/null +++ b/src/style/bellows-theme.scss @@ -0,0 +1,91 @@ +$header-background: #3498db; +$active-header-background: #2980b9; + +.bellows { + box-sizing: border-box; +} + +.bellows__header { + position: relative; + + padding: 15px 20px; + border: 1px solid $active-header-background; + border-width: 0 0 1px; + + background: $header-background; + + color: white; + + -webkit-tap-highlight-color: rgba(black, 0); + + &:active { + background: $active-header-background; + } + + &::before, + &::after { + content: ''; + + position: absolute; + top: 50%; + right: 20px; + z-index: 2; + + display: block; + width: 16px; + height: 4px; + margin-top: -2px; + + background: white; + + pointer-events: none; + + transition: transform 0.25s ease-in-out; + } + + &::before { + content: ''; + + transform: rotate(0deg); + } + + &::after { + transform: rotate(90deg); + } + + .bellows__item.bellows--is-open > &, + .bellows__item.bellows--is-opening > & { + &::before { + transform: rotate(180deg); + } + + &::after { + transform: rotate(360deg); + } + + } + + .bellows__item.bellows--is-closing > & { + + } + + .bellows__item:last-child > & { + border-bottom: 0; + } + + h1, + h2, + h3, + h4 { + margin: 0; + } +} + +.bellows__content { + padding: 20px; + border: 1px solid #ecf0f1; + + .bellows { + margin-top: 20px; + } +} \ No newline at end of file diff --git a/src/style/bellows.scss b/src/style/bellows.scss new file mode 100644 index 0000000..1d55ef3 --- /dev/null +++ b/src/style/bellows.scss @@ -0,0 +1,18 @@ +// Don't show the Bellows content before our script has initialized +.bellows__item:not(.bellows--is-open) { + > .bellows__content { + display: none; + } +} + +.bellows__item.bellows--is-open, +.bellows__item.bellows--is-closing { + > .bellows__content-wrapper { + // This declaration is necessary for items that are open by default + display: block + } +} + +.bellows__content-wrapper { + display: none; +} \ No newline at end of file diff --git a/tasks/config/autoprefixer.js b/tasks/config/autoprefixer.js new file mode 100644 index 0000000..a965c9d --- /dev/null +++ b/tasks/config/autoprefixer.js @@ -0,0 +1,11 @@ +module.exports = function(grunt) { + return { + options: { + browsers: ['last 4 versions', 'ie 8', 'ie 9', 'Android 2.3'] + }, + multiple_files: { + flatten: true, + src: 'dist/*.css' + } + }; +}; \ No newline at end of file diff --git a/tasks/config/connect.js b/tasks/config/connect.js new file mode 100644 index 0000000..f479fdb --- /dev/null +++ b/tasks/config/connect.js @@ -0,0 +1,18 @@ +module.exports = function(grunt) { + return { + server: { + options: { + hostname: '0.0.0.0', + port: 3000, + base: '.' + } + }, + test: { + options: { + hostname: '0.0.0.0', + port: 8888, + base: '.' + } + } + }; +}; \ No newline at end of file diff --git a/tasks/config/copy.js b/tasks/config/copy.js new file mode 100644 index 0000000..ec37461 --- /dev/null +++ b/tasks/config/copy.js @@ -0,0 +1,15 @@ +module.exports = function(grunt) { + return { + main: { + files: [ + { + expand: true, + flatten: true, + src: ['src/**/*.js'], + dest: 'dist/', + filter: 'isFile' + } + ] + } + }; +}; \ No newline at end of file diff --git a/tasks/config/cssmin.js b/tasks/config/cssmin.js new file mode 100644 index 0000000..f0b595d --- /dev/null +++ b/tasks/config/cssmin.js @@ -0,0 +1,12 @@ +module.exports = function(grunt) { + return { + core: { + src: 'dist/bellows.css', + dest: 'dist/bellows.min.css' + }, + style: { + src: 'dist/bellows-theme.css', + dest: 'dist/bellows-theme.min.css' + } + }; +}; \ No newline at end of file diff --git a/tasks/config/jscs.js b/tasks/config/jscs.js new file mode 100644 index 0000000..99a4683 --- /dev/null +++ b/tasks/config/jscs.js @@ -0,0 +1,22 @@ +module.exports = function(grunt) { + var jslint = require('../jslinting'); + + return { + prod: { + src: jslint.targets, + options: { + force: false, + config: 'node_modules/mobify-code-style/javascript/.jscsrc', + excludeFiles: jslint.excludes + } + }, + dev: { + src: jslint.targets, + options: { + force: true, + config: 'node_modules/mobify-code-style/javascript/.jscsrc', + excludeFiles: jslint.excludes + } + } + }; +}; \ No newline at end of file diff --git a/tasks/config/jshint.js b/tasks/config/jshint.js new file mode 100644 index 0000000..44b2e97 --- /dev/null +++ b/tasks/config/jshint.js @@ -0,0 +1,23 @@ +module.exports = function(grunt) { + var jslint = require('../jslinting'); + var jshintrcPath = 'node_modules/mobify-code-style/javascript/.jshintrc'; + + return { + prod: { + src: grunt.option('file') || jslint.targets, + options: { + force: false, + ignores: jslint.excludes, + jshintrc: jshintrcPath + } + }, + dev: { + src: grunt.option('file') || jslint.targets, + options: { + force: true, + ignores: jslint.excludes, + jshintrc: jshintrcPath + } + } + }; +}; \ No newline at end of file diff --git a/tasks/config/mocha_phantomjs.js b/tasks/config/mocha_phantomjs.js new file mode 100644 index 0000000..ccf0e1f --- /dev/null +++ b/tasks/config/mocha_phantomjs.js @@ -0,0 +1,9 @@ +module.exports = function(grunt) { + return { + all: { + options: { + urls: ['http://localhost:' + (grunt.option('p') || 8888) + '/tests/runner/'] + } + } + }; +}; \ No newline at end of file diff --git a/tasks/config/sass.js b/tasks/config/sass.js new file mode 100644 index 0000000..f144d56 --- /dev/null +++ b/tasks/config/sass.js @@ -0,0 +1,16 @@ +module.exports = function(grunt) { + return { + dist: { + options: { + style: 'nested' + }, + files: [{ + expand: true, + cwd: 'src/style', + src: ['*.scss'], + dest: 'dist', + ext: '.css' + }] + } + }; +}; \ No newline at end of file diff --git a/tasks/config/shell.js b/tasks/config/shell.js new file mode 100644 index 0000000..4097656 --- /dev/null +++ b/tasks/config/shell.js @@ -0,0 +1,8 @@ +module.exports = function(grunt) { + return { + tagRelease: { + command: 'git tag -a <%= releaseName %> -m "<%= releaseMessage %>" &&' + + 'git push origin <%= releaseName %>' + } + }; +}; \ No newline at end of file diff --git a/tasks/config/uglify.js b/tasks/config/uglify.js new file mode 100644 index 0000000..6c605f7 --- /dev/null +++ b/tasks/config/uglify.js @@ -0,0 +1,11 @@ +module.exports = function(grunt) { + return { + options: { + banner: '/*! <%= pkg.name %> <%= pkg.version %> <%= grunt.template.today("yyyy-mm-dd") %> */\n' + }, + build: { + src: 'src/js/bellows.js', + dest: 'dist/bellows.min.js' + } + }; +}; \ No newline at end of file diff --git a/tasks/config/watch.js b/tasks/config/watch.js new file mode 100644 index 0000000..c08cbbd --- /dev/null +++ b/tasks/config/watch.js @@ -0,0 +1,6 @@ +module.exports = function(grunt) { + return { + files: ["src/**/*"], + tasks: ['build-dist'] + }; +}; \ No newline at end of file diff --git a/tasks/jscs/tasks.js b/tasks/jscs/tasks.js new file mode 100644 index 0000000..efa8b04 --- /dev/null +++ b/tasks/jscs/tasks.js @@ -0,0 +1,4 @@ + +module.exports = function(grunt) { + grunt.loadNpmTasks("grunt-jscs-checker"); +}; \ No newline at end of file diff --git a/tasks/jshint/tasks.js b/tasks/jshint/tasks.js new file mode 100644 index 0000000..41bcc80 --- /dev/null +++ b/tasks/jshint/tasks.js @@ -0,0 +1,4 @@ + +module.exports = function(grunt) { + grunt.loadNpmTasks('grunt-contrib-jshint'); +}; \ No newline at end of file diff --git a/tasks/jslinting.js b/tasks/jslinting.js new file mode 100644 index 0000000..41be6a7 --- /dev/null +++ b/tasks/jslinting.js @@ -0,0 +1,6 @@ +module.exports = { + targets: [ + 'src/js/bellows.js' + ], + excludes: [] +}; \ No newline at end of file diff --git a/tasks/lint/tasks.js b/tasks/lint/tasks.js new file mode 100644 index 0000000..5928284 --- /dev/null +++ b/tasks/lint/tasks.js @@ -0,0 +1,6 @@ + +module.exports = function(grunt) { + grunt.registerTask('lint:dev', ['jshint:dev', 'jscs:dev']); + grunt.registerTask('lint:prod', ['jshint:prod', 'jscs:prod']); + grunt.registerTask('lint', ['lint:dev']); +}; \ No newline at end of file diff --git a/templates/bellows.dust b/templates/bellows.dust new file mode 100644 index 0000000..ce03831 --- /dev/null +++ b/templates/bellows.dust @@ -0,0 +1,14 @@ +{?items} +
+ {#items} +
+
+ {header} +
+
+ {content} +
+
+ {/items} +
+{/items} diff --git a/templates/bellows.mustache b/templates/bellows.mustache new file mode 100644 index 0000000..513a539 --- /dev/null +++ b/templates/bellows.mustache @@ -0,0 +1,14 @@ + +
+ {{#items}} +
+
+ {{header}} +
+
+ {{content}} +
+
+ {{/items}} +
+ diff --git a/templates/templateSchema.js b/templates/templateSchema.js new file mode 100644 index 0000000..ecc1a8b --- /dev/null +++ b/templates/templateSchema.js @@ -0,0 +1,20 @@ +/* + Below is a sample of a model used in conjunction with a bellows template. The template expects the model + will have an items property, which is an array of bellows items. Each item should have, at minimum, a + header, content, and an isOpen property denoting whether the item is open on render. + */ + +var model = { + items: [ + { + header: '', + content: '', + isOpen: false + }, + { + header: '', + content: '', + isOpen: false + } + ] +} diff --git a/tests/fixtures/bellows.html b/tests/fixtures/bellows.html new file mode 100644 index 0000000..02369ea --- /dev/null +++ b/tests/fixtures/bellows.html @@ -0,0 +1,10 @@ +
+
+
Header
+
Content
+
+
+
Header
+
Content
+
+
\ No newline at end of file diff --git a/tests/gremlins/config.js b/tests/gremlins/config.js new file mode 100644 index 0000000..167ecaa --- /dev/null +++ b/tests/gremlins/config.js @@ -0,0 +1,15 @@ +require.config({ + baseUrl: '../../', + paths: { + 'text': 'bower_components/requirejs-text/text', + 'gremlins': 'bower_components/gremlins.js/gremlins.min', + '$': 'lib/zeptojs/dist/zepto.min', + 'velocity': 'bower_components/velocity/velocity', + 'bellows': 'dist/bellows' + }, + 'shim': { + 'selectorEngine': { + exports: '$' + } + } +}); diff --git a/tests/gremlins/default.html b/tests/gremlins/default.html new file mode 100644 index 0000000..2934265 --- /dev/null +++ b/tests/gremlins/default.html @@ -0,0 +1,61 @@ + + + + Gremlins tests with bellows running 10000 iterations every 10ms + + + + + + + + + + +

Bellows Examples

+ +

Default Bellows

+ +
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+
+

Header

+
+
+

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+
+
+
+ +

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+ +

Lorem honeyed locusts sit amet, none so wise, sed do eiusmod never resting ut labore et dolore magna aliqua. Manhood death before disgrace warrior, feed it to the goats spare me your false courtesy commodo consequat. Mace aute irure dolor in reprehenderit poison is a woman's weapon lord of light tower dwarf. The last of the dragons royal, godswood garron sister betrothed officia deserunt mollit anim id est snow.

+ + + + + diff --git a/tests/gremlins/main.js b/tests/gremlins/main.js new file mode 100644 index 0000000..0d7fd38 --- /dev/null +++ b/tests/gremlins/main.js @@ -0,0 +1,22 @@ +require(['config'], function() { + require([ + 'gremlins', + 'selectorEngine', + 'bellows' + ], + function(gremlins, $) { + // Default bellows initialization (/examples/default.html) + $('.bellows.default').bellows(); + + // Enable active states + $(document).on('touchstart', function() {}); + + gremlins + .createHorde() + .gremlin(gremlins.species.clicker().clickTypes(['click']) + .canClick(function(element) { + return $(element).parents('.bellows').length; + })) + .unleash({ nb: 10000 }); + }); +}); diff --git a/tests/runner/config.js b/tests/runner/config.js new file mode 100644 index 0000000..20d460a --- /dev/null +++ b/tests/runner/config.js @@ -0,0 +1,23 @@ +require.config({ + baseUrl: '../../', + paths: { + 'text': 'bower_components/requirejs-text/text', + 'fixtures': 'tests/fixtures', + '$': 'lib/zeptojs/dist/zepto.min', + 'velocity': 'bower_components/velocity/velocity', + 'chai': 'node_modules/chai/chai', + 'mocha': 'node_modules/mocha/mocha', + 'bellows': 'dist/bellows' + }, + 'shim': { + 'mocha': { + init: function() { + this.mocha.setup('bdd'); + return this.mocha; + } + }, + '$': { + exports: '$' + } + } +}); diff --git a/tests/runner/index.html b/tests/runner/index.html new file mode 100644 index 0000000..7eb1026 --- /dev/null +++ b/tests/runner/index.html @@ -0,0 +1,13 @@ + + + + Bellows Tests + + + + + +
+ + + \ No newline at end of file diff --git a/tests/runner/testRunner.js b/tests/runner/testRunner.js new file mode 100644 index 0000000..38f9781 --- /dev/null +++ b/tests/runner/testRunner.js @@ -0,0 +1,26 @@ + +require(['config'], function() { + require([ + 'require', + 'chai', + 'mocha' + ], + function(require, chai, mocha) { + + var tests = [ + '../../tests/unit/constructor.js', + '../../tests/unit/plugin.js', + '../../tests/unit/options.js', + '../../tests/unit/events.js' + ]; + + require(tests, function() { + assert = chai.assert; + + if (window.mochaPhantomJS) { + return window.mochaPhantomJS.run(); + } + mocha.run(); + }); + }); +}); diff --git a/tests/unit/constructor.js b/tests/unit/constructor.js new file mode 100644 index 0000000..81a07e4 --- /dev/null +++ b/tests/unit/constructor.js @@ -0,0 +1,22 @@ +define([ + 'text!fixtures/bellows.html', + '$', + 'velocity', + 'bellows' +], function(fixture, $) { + var Bellows; + var element; + + describe('Bellows constructor', function() { + beforeEach(function() { + Bellows = $.fn.bellows.Constructor; + element = $(fixture); + }); + + it('creates a bellows instance', function() { + var bellows = new Bellows(element); + + assert.isDefined(bellows); + }); + }); +}); \ No newline at end of file diff --git a/tests/unit/events.js b/tests/unit/events.js new file mode 100644 index 0000000..7223970 --- /dev/null +++ b/tests/unit/events.js @@ -0,0 +1,60 @@ +define([ + 'text!fixtures/bellows.html', + '$', + 'velocity', + 'bellows' +], function(fixture, $) { + var element; + + describe('Bellows events', function() { + beforeEach(function() { + element = $(fixture); + }); + + it('fires the open event when the header is clicked', function(done) { + element.bellows({ + open: function() { + done(); + } + }); + + element.find('.bellows__header').first().trigger('click'); + }); + + it('fires the opened event when the header is clicked', function(done) { + element.bellows({ + opened: function() { + done(); + } + }); + + element.find('.bellows__header').first().trigger('click'); + }); + + it('fires the close event when the header is clicked', function(done) { + element.bellows({ + opened: function(e, ui) { + ui.item.find('.bellows__header').trigger('click'); + }, + close: function() { + done(); + } + }); + + element.find('.bellows__header').first().trigger('click'); + }); + + it('fires the closed event when the header is clicked', function(done) { + element.bellows({ + opened: function(e, ui) { + ui.item.find('.bellows__header').trigger('click'); + }, + closed: function() { + done(); + } + }); + + element.find('.bellows__header').first().trigger('click'); + }); + }); +}); \ No newline at end of file diff --git a/tests/unit/options.js b/tests/unit/options.js new file mode 100644 index 0000000..0f0d2e1 --- /dev/null +++ b/tests/unit/options.js @@ -0,0 +1,121 @@ +define([ + 'text!fixtures/bellows.html', + '$', + 'velocity', + 'bellows' +], function(fixture, $) { + var Bellows; + var element; + + describe('Bellows options', function() { + beforeEach(function() { + Bellows = $.fn.bellows.Constructor; + element = $(fixture); + }); + + describe('creates default options when no options parameter not used', function() { + it('correctly defines singleItemOpen', function() { + var bellows = new Bellows(element); + + assert.isFalse(bellows.options.singleItemOpen); + assert.isBoolean(bellows.options.singleItemOpen); + }); + + it('correctly defines duration', function() { + var bellows = new Bellows(element); + + assert.equal(bellows.options.duration, 200); + assert.isNumber(bellows.options.duration); + }); + + it('correctly defines easing', function() { + var bellows = new Bellows(element); + + assert.equal(bellows.options.easing, 'swing'); + assert.isString(bellows.options.easing); + }); + + it('correctly defines events', function() { + var bellows = new Bellows(element); + + assert.isFunction(bellows.options.open); + assert.isFunction(bellows.options.opened); + assert.isFunction(bellows.options.close); + assert.isFunction(bellows.options.closed); + }); + }); + + describe('creates custom options when options parameter used', function() { + it('correctly defines singleItemOpen as true', function() { + var bellows = new Bellows(element, { singleItemOpen: true }); + + assert.isTrue(bellows.options.singleItemOpen); + assert.isBoolean(bellows.options.singleItemOpen); + }); + + it('correctly defines duration of 400', function() { + var bellows = new Bellows(element, { duration: 400 }); + + assert.equal(bellows.options.duration, 400); + assert.isNumber(bellows.options.duration); + }); + + it('correctly defines easing as ease-in-out', function() { + var bellows = new Bellows(element, { easing: 'ease-in-out'}); + + assert.equal(bellows.options.easing, 'ease-in-out'); + assert.isString(bellows.options.easing); + }); + + it('correctly defines open event', function() { + var open = function() { + console.log('I\'m open!') + }; + var bellows = new Bellows(element, { open: open }); + + assert.equal(bellows.options.open, open); + assert.isFunction(bellows.options.open); + }); + + it('correctly defines open event', function() { + var open = function() { + console.log('Open!') + }; + var bellows = new Bellows(element, { open: open }); + + assert.equal(bellows.options.open, open); + assert.isFunction(bellows.options.open); + }); + + it('correctly defines opened event', function() { + var opened = function() { + console.log('Opened!') + }; + var bellows = new Bellows(element, { opened: opened }); + + assert.equal(bellows.options.opened, opened); + assert.isFunction(bellows.options.opened); + }); + + it('correctly defines close event', function() { + var close = function() { + console.log('Close!') + }; + var bellows = new Bellows(element, { close: close }); + + assert.equal(bellows.options.close, close); + assert.isFunction(bellows.options.close); + }); + + it('correctly defines closed event', function() { + var closed = function() { + console.log('Closed!') + }; + var bellows = new Bellows(element, { closed: closed }); + + assert.equal(bellows.options.closed, closed); + assert.isFunction(bellows.options.closed); + }); + }); + }); +}); \ No newline at end of file diff --git a/tests/unit/plugin.js b/tests/unit/plugin.js new file mode 100644 index 0000000..d8d2667 --- /dev/null +++ b/tests/unit/plugin.js @@ -0,0 +1,93 @@ +define([ + 'text!fixtures/bellows.html', + '$', + 'velocity', + 'bellows' +], function(fixture, $) { + var element; + + describe('Bellows plugin', function() { + beforeEach(function() { + element = $(fixture); + }); + + describe('binding to Zepto\'s fn', function() { + it('defines bellows in Zepto', function() { + var bellows = $.fn.bellows; + + assert.isDefined(bellows); + }); + + it('defines bellows as a function', function() { + var bellows = $.fn.bellows; + + assert.isFunction(bellows); + }); + }); + + describe('invoking bellows', function() { + it('creates bellows instance on element', function() { + element.bellows(); + + assert.isDefined(element.data('bellows')); + }); + + it('stores element inside instance', function() { + element.bellows(); + + assert.isDefined(element.data('bellows').$bellows); + }); + + it('wraps each content section with correct CSS class', function() { + element.bellows(); + + assert.lengthOf(element.find('.bellows__content-wrapper'), 2); + }); + }); + + describe('invoking bellows methods before plugin is initialized', function() { + it('throws when not initialized', function() { + assert.throws(function() { element.bellows('open'); }); + }); + }); + + describe('invoking bellows methods using the plugin interface', function() { + it('opens a bellows item using the open method', function(done) { + element.bellows({ + opened: function(e, ui) { + assert.isTrue(ui.item.hasClass('bellows--is-open')); + done(); + } + }); + + element.bellows('open', 0); + }); + + it('closes a bellows item using the close method', function(done) { + element.bellows({ + opened: function() { + element.bellows('close', 0); + }, + closed: function(e, ui) { + assert.isFalse(ui.item.hasClass('bellows--is-open')); + done(); + } + }); + + element.bellows('open', 0); + }); + + it('throws for method calls that don\'t exist', function() { + assert.throws(function() { element.bellows().bellows('noMethod'); }); + }); + + it('throws when attempting to invoke private methods', function() { + assert.throws(function() { element.bellows().bellows('_init'); }); + }); + + it('throws when attempting to invoke methods that aren\'t functions', function() { + assert.throws(function() { element.bellows().bellows('singleItemOpen'); }); + }); + }); + }); +}); \ No newline at end of file