From c7e424af9f630a9283447b773ac588965bb1dd3e Mon Sep 17 00:00:00 2001 From: Karl Hudgell Date: Thu, 28 Jan 2016 08:24:06 +0000 Subject: [PATCH] first grid push --- GRID/.bowerrc | 4 + GRID/.gitignore | 7 + GRID/CHANGELOG.md | 185 + GRID/CONTRIBUTING.md | 143 + GRID/Gruntfile.js | 183 + GRID/LICENSE | 19 + GRID/README.md | 50 + GRID/assets/demo.css | 69 + GRID/assets/gridster_new.js | 104 + GRID/assets/jquery.gridster.js | 3232 ++++++ GRID/assets/jquery.js | 9404 ++++++++++++++++++ GRID/dist/jquery.gridster.css | 121 + GRID/dist/jquery.gridster.js | 3987 ++++++++ GRID/dist/jquery.gridster.min.css | 2 + GRID/dist/jquery.gridster.min.js | 2 + GRID/dist/jquery.gridster.with-extras.js | 4163 ++++++++ GRID/dist/jquery.gridster.with-extras.min.js | 2 + GRID/grid.php | 46 + GRID/jquery.gridder.html | 46 + GRID/jquery.gridder_test.js | 38 + GRID/package.json | 41 + GRID/src/jquery.collision.js | 242 + GRID/src/jquery.coords.js | 125 + GRID/src/jquery.draggable.js | 432 + GRID/src/jquery.gridster.css | 117 + GRID/src/jquery.gridster.extras.js | 175 + GRID/src/jquery.gridster.js | 3140 ++++++ GRID/src/utils.js | 72 + Pic/sickrage.png | Bin 116971 -> 130497 bytes assets/css/demo.css | 69 + assets/css/grid.css | 69 + assets/css/jquery.gridster.css | 64 + assets/css/jquery.gridster.min.css | 3 + assets/css/styles.css | 60 + assets/gridster_new.js | 107 + assets/jquery.gridster.js | 3232 ++++++ assets/jquery.js | 9404 ++++++++++++++++++ feeds/kknowledgefeed.php | 5 +- feeds/webclipfeed.php | 2 +- grid.php | 60 + links/jdownloader.php | 20 +- links/transmission.php | 5 +- links/utorrent.php | 7 +- newtest.php | 160 + right.php | 2 +- robots.txt | 5 + 46 files changed, 39413 insertions(+), 12 deletions(-) create mode 100644 GRID/.bowerrc create mode 100644 GRID/.gitignore create mode 100644 GRID/CHANGELOG.md create mode 100644 GRID/CONTRIBUTING.md create mode 100644 GRID/Gruntfile.js create mode 100644 GRID/LICENSE create mode 100644 GRID/README.md create mode 100644 GRID/assets/demo.css create mode 100644 GRID/assets/gridster_new.js create mode 100644 GRID/assets/jquery.gridster.js create mode 100644 GRID/assets/jquery.js create mode 100644 GRID/dist/jquery.gridster.css create mode 100644 GRID/dist/jquery.gridster.js create mode 100644 GRID/dist/jquery.gridster.min.css create mode 100644 GRID/dist/jquery.gridster.min.js create mode 100644 GRID/dist/jquery.gridster.with-extras.js create mode 100644 GRID/dist/jquery.gridster.with-extras.min.js create mode 100644 GRID/grid.php create mode 100644 GRID/jquery.gridder.html create mode 100644 GRID/jquery.gridder_test.js create mode 100644 GRID/package.json create mode 100644 GRID/src/jquery.collision.js create mode 100644 GRID/src/jquery.coords.js create mode 100644 GRID/src/jquery.draggable.js create mode 100644 GRID/src/jquery.gridster.css create mode 100644 GRID/src/jquery.gridster.extras.js create mode 100644 GRID/src/jquery.gridster.js create mode 100644 GRID/src/utils.js create mode 100644 assets/css/demo.css create mode 100644 assets/css/grid.css create mode 100644 assets/css/jquery.gridster.css create mode 100644 assets/css/jquery.gridster.min.css create mode 100644 assets/css/styles.css create mode 100644 assets/gridster_new.js create mode 100644 assets/jquery.gridster.js create mode 100644 assets/jquery.js create mode 100644 grid.php create mode 100644 newtest.php create mode 100644 robots.txt diff --git a/GRID/.bowerrc b/GRID/.bowerrc new file mode 100644 index 0000000..a33fd7a --- /dev/null +++ b/GRID/.bowerrc @@ -0,0 +1,4 @@ +{ + "directory": "libs", + "json": "package.json" +} \ No newline at end of file diff --git a/GRID/.gitignore b/GRID/.gitignore new file mode 100644 index 0000000..440a178 --- /dev/null +++ b/GRID/.gitignore @@ -0,0 +1,7 @@ +node_modules/ +libs/ +gh-pages/ +demo/ +.idea +.DS_Store +.idea diff --git a/GRID/CHANGELOG.md b/GRID/CHANGELOG.md new file mode 100644 index 0000000..6d6f1a2 --- /dev/null +++ b/GRID/CHANGELOG.md @@ -0,0 +1,185 @@ + +### v0.5.6 (2014-09-25) + + +#### Bug Fixes + +* **draggable:** namespace events with unique ids ([79aff38c](http://github.com/ducksboard/gridster.js/commit/79aff38c60cc6ce2c0f0160bd3c6f93cb2511642)) + + +### v0.5.5 (2014-07-25) + + +#### Bug Fixes + +* **gridster:** fire `positionschanged` when widget orig position changes ([9926ceff](http://github.com/ducksboard/gridster.js/commit/9926ceff59cba49c71542e45aa095be35eb1df58)) + + +### v0.5.4 (2014-07-16) + + +#### Bug Fixes + +* **gridster:** serialize returns an Array object, not a jQuery object ([93df6cf6](http://github.com/ducksboard/gridster.js/commit/93df6cf6907fd0fb8787b3d068c9a9c467dcc020), closes [#394](http://github.com/ducksboard/gridster.js/issues/394)) + + +### v0.5.3 (2014-07-04) + + +#### Bug Fixes + +* **gridster:** + * custom `ignore_dragging` overwrites the default value ([6bcfa6e1](http://github.com/ducksboard/gridster.js/commit/6bcfa6e16e4a88cbb5efff1ce29308737884a89d)) + * sort widgets appropriately when reading them from DOM ([5c6d25cb](http://github.com/ducksboard/gridster.js/commit/5c6d25cbbe3de021806408f3cff6cb1e139c0a25)) + + +#### Features + +* make gridster AMD compatible ([589d7fd5](http://github.com/ducksboard/gridster.js/commit/589d7fd509a570fd02666c2f8231545211d6c83f)) +* **gridster:** move widget up when added if there is space available ([8ec307b6](http://github.com/ducksboard/gridster.js/commit/8ec307b6f7173e94610409adcb1671372cc2c67d)) + + +### v0.5.2 (2014-06-16) + + +#### Bug Fixes + +* **draggable:** + * handle both touch and click events ([021a6c23](http://github.com/ducksboard/gridster.js/commit/021a6c23e851210c1b817bd353a1e5e19ce10b90), closes [#207](http://github.com/ducksboard/gridster.js/issues/207), [#236](http://github.com/ducksboard/gridster.js/issues/236), [#329](http://github.com/ducksboard/gridster.js/issues/329), [#380](http://github.com/ducksboard/gridster.js/issues/380)) + * replaced scrollX/Y with scrollLeft/Top ([bb7463a3](http://github.com/ducksboard/gridster.js/commit/bb7463a3241750397492dfbac133cea193f0254f)) + * fix offset during drag ([c726c4ad](http://github.com/ducksboard/gridster.js/commit/c726c4ad9c18fea95e4b46b9bacd36c42aa9691c)) + * bind drag events to $document ([dd6c7420](http://github.com/ducksboard/gridster.js/commit/dd6c7420087d5810a9f6b02bf9d81a04a60ae840)) +* **gridster:** + * fix add_widget to use correct size_y when adding rows ([7d22e6c8](http://github.com/ducksboard/gridster.js/commit/7d22e6c8b201de33e33def77a93dc9009d0aa4cb)) + * Removing previously added style tags before adding new one. ([93c46ff4](http://github.com/ducksboard/gridster.js/commit/93c46ff45ebe59f3658b7f32f05b67109aa87311)) + + +#### Features + +* **draggable:** + * allow ignore_dragging config option to be a function ([69fcfe45](http://github.com/ducksboard/gridster.js/commit/69fcfe459678e833cb53de040b9fbc96dd687543)) + * option to not remove helper on drag stop ([03910df9](http://github.com/ducksboard/gridster.js/commit/03910df967a1ae7bcb2fa3aadd58255e0bcbf327)) + + +### v0.5.1 (2014-03-05) + + +#### Features + +* **collision:** overlapping region as a config option ([720d487e](http://github.com/ducksboard/gridster.js/commit/720d487e3988593e2c60909c88aaff13fbd4f842)) +* **coords:** + * allow both (left/x1) and (top/y1) attr keys ([6f22217f](http://github.com/ducksboard/gridster.js/commit/6f22217f056e4fc52f6405f2af49596105aae150)) + * add destroy method ([fdeee4f6](http://github.com/ducksboard/gridster.js/commit/fdeee4f636266c7a0579ced833f04fec013b6863)) +* **draggable:** keep container position prop if different than static ([04868a38](http://github.com/ducksboard/gridster.js/commit/04868a384d655d110f2d153d2fddb94b1c6d54a9)) +* **gridster:** destroy element's data and optionally remove from DOM ([dc09f191](http://github.com/ducksboard/gridster.js/commit/dc09f191d8503669cfa4737122c77cb0f5b9c3d2)) + + +## v0.5.0 (2014-02-14) + + +#### Bug Fixes + +* **autogrow:** refining autogrow_cols behavior and grid width issues ([835c2df8](http://github.com/ducksboard/gridster.js/commit/835c2df84419a92b1641b687fcf083f3ff102627)) +* **resize.stop:** Call resize.stop at the latest possible moment ([e21f63a0](http://github.com/ducksboard/gridster.js/commit/e21f63a05a539f5c611eb49cd6861b1e38b36531)) + + +#### Features + +* **draggable:** Add toggle draggable method. ([073fdc40](http://github.com/ducksboard/gridster.js/commit/073fdc40e0a94dd371646fc54cd420e3ddab0254)) + + +### v0.4.4 (2014-02-13) + + +#### Features + +* **resize:** add start/stop/resize event triggers ([7ca8deec](http://github.com/ducksboard/gridster.js/commit/7ca8deec8559d950097a6dc351cb0c6fcef3458d)) + + +### v0.4.3 (2014-02-11) + + +#### Bug Fixes + +* **generated-styles:** cleaning cached serializations properly ([f8b04f29](http://github.com/ducksboard/gridster.js/commit/f8b04f298e12e46ca9b07f0bae0abc6b08ed6e18)) + + +### v0.4.2 (2014-02-07) + + +#### Bug Fixes + +* recalculate grid width when adding widgets ([47745978](http://github.com/ducksboard/gridster.js/commit/4774597834300601fc81d5111a31a8c1672c55e1)) + + +### v0.4.1 (2014-02-07) + +#### Bug Fixes + +* add resize.min_size option to default config object ([5672edb0](http://github.com/ducksboard/gridster.js/commit/5672edb05e39c6b9ff5e3ca31d68c9e94dfaa617)) + + +## v0.4.0 (2014-02-07) + + +#### Bug Fixes + +* **gridster:** + * leaking options with multiple Gridster instances ([07c71097](http://github.com/ducksboard/gridster.js/commit/07c7109771094d98be51d68448a20e1d2987b35d)) + * resize.axes default option only 'both' ([62988780](http://github.com/ducksboard/gridster.js/commit/6298878077d5db129daa9780939fec5237b82af9)) +* **licenses:** add required copyright message for underscore ([b563c094](http://github.com/ducksboard/gridster.js/commit/b563c094cf0f3a5da2288492f95759ae32e8967c)) +* **readme:** link title jsfiddle -> jsbin, edit 5) of process steps ([0641aa89](http://github.com/ducksboard/gridster.js/commit/0641aa89833ecf9d167f7d8e89ee8bd5b4304248)) + + +#### Features + +* **draggable:** + * method to set drag limits dynamically ([d4482ec1](http://github.com/ducksboard/gridster.js/commit/d4482ec1476f8a0b6fb6cdeb25b7774ef678d81c)) + * support horizontal scrolling while dragging ([ae4921b7](http://github.com/ducksboard/gridster.js/commit/ae4921b70798944211267cacf8a89e62d0818369)) +* **gridster:** increase grid width when dragging or resizing ([37c4e943](http://github.com/ducksboard/gridster.js/commit/37c4e94358b9392710452b9e7f96454837bf9845)) +* **resize:** add option to set min_size of a widget ([ff511872](http://github.com/ducksboard/gridster.js/commit/ff511872e65992ee89bd2a88d862caaf99733f38)) + + +## v0.3.0 (2013-11-18) + + +#### Features + +* **draggable:** + * method to set drag limits dynamically ([d4482ec1](http://github.com/ducksboard/gridster.js/commit/d4482ec1476f8a0b6fb6cdeb25b7774ef678d81c)) + * support horizontal scrolling while dragging ([ae4921b7](http://github.com/ducksboard/gridster.js/commit/ae4921b70798944211267cacf8a89e62d0818369)) +* **gridster:** increase grid width when dragging or resizing ([b61df653](http://github.com/ducksboard/gridster.js/commit/b61df6535f728970fb8c6f25a208275dbde66550)) + + +### v0.2.1 (2013-10-28) + + +#### Features + +* **resize:** Add start/stop/resize callbacks ([d4ec7140](http://github.com/ducksboard/gridster.js/commit/d4ec7140f736bc30697c75b54ed3242ddf1d75b9)) + + +## v0.2.0 (2013-10-26) + + +#### Bug Fixes + +* fixes and improvements in widget-resizing. ([ae02b32b](http://github.com/ducksboard/gridster.js/commit/ae02b32b9210c6328f4acc339e215ae50c134f77), closes [#32](http://github.com/ducksboard/gridster.js/issues/32)) +* **gridster:** + * the preview holder should not always use `li` ([1ade74e2](http://github.com/ducksboard/gridster.js/commit/1ade74e239485b07e870fca44e1eafb3ff1ae283)) + * overlapping widget problem ([31fd8d6b](http://github.com/ducksboard/gridster.js/commit/31fd8d6ba893e4c39b91ba30d429e37f3da30b24)) + * Orphan preview holder when dragging is interrupted ([1b13617d](http://github.com/ducksboard/gridster.js/commit/1b13617df2ce53235bdf3a1e38f1555f529663c3)) + * remove_widget Returns the instance of the Gridster Class ([5bfbc5c0](http://github.com/ducksboard/gridster.js/commit/5bfbc5c0b5ab49c2a7c651327ce2e0f30f594985)) + + +#### Features + +* **draggable:** + * new config option to move or not the dragged element ([4d9b2a84](http://github.com/ducksboard/gridster.js/commit/4d9b2a84f11cb7cb2ddad51c158d92b82e7bc447)) + * CSS selectors support in `ignore_dragging` config opt ([0f956249](http://github.com/ducksboard/gridster.js/commit/0f95624925be97aee7a8450707e04e887e4dac58)) + * pass previous position to the drag callback ([055cc0e4](http://github.com/ducksboard/gridster.js/commit/055cc0e4f6f9de5721986515656ac894855f9e02)) + * Don't start new drag if previous one hasn't stopped ([91ca6572](http://github.com/ducksboard/gridster.js/commit/91ca65721c2eb32b5dec82cdc5e5e7f81dac329e)) + * pass useful data to all drag callbacks ([8dda2410](http://github.com/ducksboard/gridster.js/commit/8dda2410f300592706985c05141ca6b702977dc0)) +* **gridster:** drag-and-drop widget resizing ([e1924053](http://github.com/ducksboard/gridster.js/commit/e19240532de0bad35ffe6e5fc63934819390adc5)) +* **utils:** add delay helper to utils ([faa6c5db](http://github.com/ducksboard/gridster.js/commit/faa6c5db0002feccf681e9f919ed583eef152773)) + diff --git a/GRID/CONTRIBUTING.md b/GRID/CONTRIBUTING.md new file mode 100644 index 0000000..032a9fa --- /dev/null +++ b/GRID/CONTRIBUTING.md @@ -0,0 +1,143 @@ +# Contributing to this project + +Please take a moment to review this document in order to make the contribution +process easy and effective for everyone involved. + +Following these guidelines helps to communicate that you respect the time of +the developers managing and developing this open source project. In return, +they should reciprocate that respect in addressing your issue or assessing +patches and features. + + +## Using the issue tracker + +The issue tracker is the preferred channel for [bug reports](#bugs), +[features requests](#features) and [submitting pull +requests](#pull-requests), but please respect the following restrictions: + +* Please **do not** use the issue tracker for personal support requests (use + [Stack Overflow](http://stackoverflow.com)). + +* Please **do not** derail or troll issues. Keep the discussion on topic and + respect the opinions of others. + + + +## Bug reports + +A bug is a _demonstrable problem_ that is caused by the code in the repository. +Good bug reports are extremely helpful - thank you! + +Guidelines for bug reports: + +1. **Use the GitHub issue search** — check if the issue has already been + reported. + +2. **Check if the issue has been fixed** — try to reproduce it using the + latest `master` or development branch in the repository. + +3. **Isolate the problem** — ideally create a [reduced test + case](http://css-tricks.com/6263-reduced-test-cases/) and a live example (you can use something like [jsfiddle](http://jsfiddle.net/) or [jsbin](http://jsbin.com/)) . + +A good bug report shouldn't leave others needing to chase you up for more +information. Please try to be as detailed as possible in your report. What is +your environment? What steps will reproduce the issue? What browser(s) and OS +experience the problem? What would you expect to be the outcome? All these +details will help people to fix any potential bugs. + +Example: + +> Short and descriptive example bug report title +> +> A summary of the issue and the browser/OS environment in which it occurs. If +> suitable, include the steps required to reproduce the bug. +> +> 1. This is the first step +> 2. This is the second step +> 3. Further steps, etc. +> +> `` - a link to the reduced test case +> +> Any other information you want to share that is relevant to the issue being +> reported. This might include the lines of code that you have identified as +> causing the bug, and potential solutions (and your opinions on their +> merits). + + + +## Feature requests + +Feature requests are welcome. But take a moment to find out whether your idea +fits with the scope and aims of the project. It's up to *you* to make a strong +case to convince the project's developers of the merits of this feature. Please +provide as much detail and context as possible. + +**Please, use the GitHub issue search** to check if the feature has already been requested. + + + +## Pull requests + +Good pull requests - patches, improvements, new features - are a fantastic +help. They should remain focused in scope and avoid containing unrelated +commits. + +**Please ask first** before embarking on any significant pull request (e.g. +implementing features, refactoring code, porting to a different language), +otherwise you risk spending a lot of time working on something that the +project's developers might not want to merge into the project. + +Code must follow, mostly, these [coding conventions](http://javascript.crockford.com/code.html) . + +Adhering to the following this process is the best way to get your work +included in the project: + +1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, + and configure the remotes: + + ```bash + # Clone your fork of the repo into the current directory + git clone https://github.com//gridster.js + # Navigate to the newly cloned directory + cd gridster.js + # Assign the original repo to a remote called "upstream" + git remote add upstream https://github.com/ducksboard/gridster.js + ``` + +2. If you cloned a while ago, get the latest changes from upstream: + + ```bash + git checkout master + git pull upstream master + ``` + +3. Create a new topic branch (off the main project development branch) to + contain your feature, change, or fix: + + ```bash + git checkout -b + ``` + +4. Commit your changes in logical chunks. Please adhere to these [git commit + message guidelines](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y) + or your code is unlikely be merged into the main project. Use Git's + [interactive rebase](https://help.github.com/articles/interactive-rebase) + feature to tidy up your commits before making them public. + +5. Merge or rebase the upstream development branch into your topic branch: + + ```bash + git pull --rebase upstream master + ``` + +6. Push your topic branch up to your fork: + + ```bash + git push origin + ``` + +7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) + with a clear title and description. + +**IMPORTANT**: By submitting a patch, you agree to allow the project owner to +license your work under the same license as that used by the project. diff --git a/GRID/Gruntfile.js b/GRID/Gruntfile.js new file mode 100644 index 0000000..48be913 --- /dev/null +++ b/GRID/Gruntfile.js @@ -0,0 +1,183 @@ +/*global module:false*/ +module.exports = function(grunt) { + + // Project configuration. + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + meta: { + banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' + + '<%= grunt.template.today("yyyy-mm-dd") %>\n' + + '<%= pkg.homepage ? "* " + pkg.homepage : "" %>\n' + + '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' + + ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n\n', + + minibanner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' + + '<%= grunt.template.today("yyyy-mm-dd") %> - ' + + '<%= pkg.homepage ? "* " + pkg.homepage + " - " : "" %>' + + 'Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' + + ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */ ' + }, + concat: { + options: { + stripBanners: true, + banner: '<%= meta.banner %>' + }, + dist_js: { + src: ['src/jquery.coords.js', 'src/jquery.collision.js', 'src/utils.js', 'src/jquery.draggable.js', 'src/jquery.<%= pkg.name %>.js'], + dest: 'dist/jquery.<%= pkg.name %>.js' + }, + + dist_extras_js: { + src: ['src/jquery.coords.js', 'src/jquery.collision.js', 'src/utils.js', 'src/jquery.draggable.js', 'src/jquery.<%= pkg.name %>.js', 'src/jquery.<%= pkg.name %>.extras.js'], + dest: 'dist/jquery.<%= pkg.name %>.with-extras.js' + }, + + dist_css: { + src: ['src/jquery.<%= pkg.name %>.css'], + dest: 'dist/jquery.<%= pkg.name %>.css' + }, + + dist_demo_js: { + src: ['src/jquery.coords.js', 'src/jquery.collision.js', 'src/utils.js', 'src/jquery.draggable.js', 'src/jquery.<%= pkg.name %>.js'], + dest: 'gh-pages/dist/jquery.<%= pkg.name %>.js' + }, + + dist_extras_demo_js: { + src: ['src/jquery.coords.js', 'src/jquery.collision.js', 'src/utils.js', 'src/jquery.draggable.js', 'src/jquery.<%= pkg.name %>.js', 'src/jquery.<%= pkg.name %>.extras.js'], + dest: 'gh-pages/dist/jquery.<%= pkg.name %>.with-extras.js' + }, + + dist_demo_css: { + src: ['src/jquery.<%= pkg.name %>.css'], + dest: 'gh-pages/dist/jquery.<%= pkg.name %>.css' + } + }, + uglify: { + options: { + banner: '<%= meta.minibanner %>' + }, + dist: { + files: { + 'dist/jquery.<%= pkg.name %>.min.js': ['<%= concat.dist_js.dest %>'] + } + }, + + dist_extras: { + files: { + 'dist/jquery.<%= pkg.name %>.with-extras.min.js': ['<%= concat.dist_extras_js.dest %>'] + } + }, + + dist_demo: { + files: { + 'gh-pages/dist/jquery.<%= pkg.name %>.min.js': ['<%= concat.dist_js.dest %>'], + } + }, + + dist_extras_demo: { + files: { + 'gh-pages/dist/jquery.<%= pkg.name %>.with-extras.min.js': ['<%= concat.dist_extras_js.dest %>'] + } + } + }, + cssmin: { + compress: { + options: { + keepSpecialComments: 0, + banner: '<%= meta.minibanner %>' + }, + files: { + "dist/jquery.<%= pkg.name %>.min.css": ["dist/jquery.<%= pkg.name %>.css"], + "gh-pages/dist/jquery.<%= pkg.name %>.min.css": ["dist/jquery.<%= pkg.name %>.css"] + } + } + }, + jshint: { + files: ['grunt.js', 'src/**/*.js', 'test/**/*.js'] + }, + watch: { + files: ['<%= lint.files %>', 'src/jquery.<%= pkg.name %>.css'], + tasks: 'min concat' + }, + jshint: { + options: { + curly: true, + eqeqeq: true, + immed: true, + latedef: true, + newcap: true, + noarg: true, + sub: true, + undef: true, + boss: true, + eqnull: true, + browser: true + }, + globals: { + jQuery: true + } + }, + yuidoc: { + compile: { + "name": 'gridster.js', + "description": 'gridster.js, a drag-and-drop multi-column jQuery grid plugin', + "version": '0.1.0', + "url": 'http://gridster.net/', + "logo": 'https://ducksboard.com/static/images/svg/logo-ducksboard-black-small.svg', + options: { + paths: "src/", + outdir: "gh-pages/docs/" + } + } + }, + + bump: { + options: { + files: ['package.json'], + updateConfigs: ['pkg'], + commit: true, + commitMessage: 'Release v%VERSION%', + commitFiles: ['package.json', 'CHANGELOG.md', 'dist/'], // '-a' for all files + createTag: true, + tagName: 'v%VERSION%', + tagMessage: 'Version %VERSION%', + push: false, + pushTo: 'origin', + gitDescribeOptions: '--tags --always --abbrev=1 --dirty=-d' // options to use with '$ git describe' + } + }, + + changelog: { + options: { + dest: 'CHANGELOG.md' + } + }, + + watch: { + files: ['libs/*.js', 'src/*.js', 'src/*.css', 'Gruntfile.js'], + tasks: ['concat', 'uglify', 'cssmin'] + } + }); + + + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-contrib-jshint'); + grunt.loadNpmTasks('grunt-contrib-concat'); + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-contrib-cssmin'); + grunt.loadNpmTasks('grunt-contrib-yuidoc'); + grunt.loadNpmTasks('grunt-bump'); + grunt.loadNpmTasks('grunt-conventional-changelog'); + + // Default task. + grunt.registerTask('default', ['jshint', 'concat', 'uglify', 'cssmin']); + grunt.registerTask('build', ['default']); + grunt.registerTask('docs', ['yuidoc']); + + grunt.registerTask('release', ['build', 'bump-only:patch', 'build', 'docs', 'changelog']); + grunt.registerTask('release:minor', ['build', 'bump-only:minor', 'build', 'docs', 'changelog']); + grunt.registerTask('release:major', ['build', 'bump-only:major', 'build', 'docs', 'changelog']); + grunt.registerTask('release:git', ['build', 'bump-only:git', 'build', 'docs', 'changelog', 'bump-commit']); + grunt.registerTask('release:commit', ['bump-commit']); + +}; diff --git a/GRID/LICENSE b/GRID/LICENSE new file mode 100644 index 0000000..869f81f --- /dev/null +++ b/GRID/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012 Ducksboard + +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/GRID/README.md b/GRID/README.md new file mode 100644 index 0000000..00eff37 --- /dev/null +++ b/GRID/README.md @@ -0,0 +1,50 @@ +Gridster.js +=========== + +[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/ducksboard/gridster.js/trend.png)](https://bitdeli.com/free "Bitdeli Badge") + +Gridster is a jQuery plugin that makes building intuitive draggable +layouts from elements spanning multiple columns. You can even +dynamically add and remove elements from the grid. + +More at [http://gridster.net/](http://gridster.net/). + +[Releases](https://github.com/ducksboard/gridster.js/releases) + +[CHANGELOG](https://github.com/ducksboard/gridster.js/blob/master/CHANGELOG.md) + +Gridster is maintained by Ducksboard occasionally but not actively. +@dustmoo and @pushmatrix have also write permissions as Gridster maintainers +they are. Thank you guys! + +## Forks + +Mr @dustmoo (maintainer of Gridster) has his own fork of gridster.js +with some new interesting features like widget-swapping and static widgets. + +Can be found here: [dustmoo/gridster.js](https://github.com/dustmoo/gridster.js) + +@dustmoo is working in his spare time to merge all these changes into +ducksboard/gridster.js + +If anyone would like to help @dustmoo improve his fork and reconcile +it with the main library he would be happy for the help. + + +## Contributing to this project + +Anyone and everyone is welcome to contribute. Please take a moment to review the guidelines for contributing. + +* [Bug reports](CONTRIBUTING.md#bugs) +* [Feature requests](CONTRIBUTING.md#features) +* [Pull requests](CONTRIBUTING.md#pull-requests) + + +## License + +Distributed under the MIT license. + +## Whodunit + +Gridster is built by [Ducksboard](http://ducksboard.com/) with the help of all +these [wonderful people](https://github.com/ducksboard/gridster.js/graphs/contributors). diff --git a/GRID/assets/demo.css b/GRID/assets/demo.css new file mode 100644 index 0000000..c64eef7 --- /dev/null +++ b/GRID/assets/demo.css @@ -0,0 +1,69 @@ +.gridster * { + margin:0; + padding:0; +} + +ul { + list-style-type: none; +} + + +/*/ +/* demo +/*/ + + +body { + font-size: 16px; + font-family: 'Helvetica Neue', Arial, sans-serif; + color: #444; + margin: 30px 40px; +} + +.controls { + margin-bottom: 20px; +} + +/*/ +/* gridster +/*/ + +.gridster ul { + background-color: #EFEFEF; +} + +.gridster li { + font-size: 1em; + font-weight: bold; + text-align: center; + line-height: 100%; +} + + +.gridster { + margin: 0 auto; + + opacity: .8; + + -webkit-transition: opacity .6s; + -moz-transition: opacity .6s; + -o-transition: opacity .6s; + -ms-transition: opacity .6s; + transition: opacity .6s; +} + +.gridster .gs-w { + background: #DDD; + cursor: pointer; +} + +.gridster .player { + background: #BBB; +} + + +.gridster .preview-holder { + border: none!important; + background: red!important; +} + diff --git a/GRID/assets/gridster_new.js b/GRID/assets/gridster_new.js new file mode 100644 index 0000000..c4a06af --- /dev/null +++ b/GRID/assets/gridster_new.js @@ -0,0 +1,104 @@ + + $(window).ready(function () { + var localData = JSON.parse(localStorage.getItem('positions')); + + if(localData!=null) + { + $.each(localData, function(i,value){ + + var id_name; + + id_name="#"; + id_name = id_name + value.id; + console.log(id_name); + + $(id_name).attr({"data-col":value.col, "data-row":value.row, "data-sizex":value.size_x, "data-sizey":value.size_y}); + + +}); + } + else{ + console.log('No data returned by the server'); + } + + + // widget_selector: "> ul" + // Define which elements are the widgets. Can be a CSS Selector string or a jQuery collection of HTMLElements. + + // widget_margins: [3, 3] + // Horizontal and vertical margins respectively for widgets. + + // widget_base_dimensions: [110, 110] + // Base widget dimensions in pixels. The first index is the width, the second is the height. + + var grid_canvas = $(".gridster > ul").gridster({ + widget_margins: [3, 3], + widget_base_dimensions: [110, 110], + + // serialize_params: function($w, wgd) { return { id: $($w).attr('id'),col: wgd.col, row: wgd.row,size_x: wgd.size_x,size_y: wgd.size_y } + // A function to return serialized data for each each widget, used when calling the serialize method. Two arguments are passed: + // $w: the jQuery wrapped HTMLElement which is used to get the id, and wgd: the grid coords object with keys col, row, size_x and size_y. + + + serialize_params: function($w, wgd) + { + return { + id: $($w).attr('id'), + col: wgd.col, + row: wgd.row, + size_x: wgd.size_x, + size_y: wgd.size_y, + + }; + }, + + // draggable.stop: function(event, ui){} -- A callback for when dragging stops. + // You can also implement other draggable options based on your requirements + // draggable.start: function(event, ui){} -- A callback for when dragging starts. + // draggable.drag: function(event, ui){} -- A callback for when the mouse is moved during the dragging. + + draggable: + { + stop: function(event, ui) { + + // .serialize( ) + // Creates an array of objects representing the current position of all widgets in the grid. + // Returns an Array of Objects (ready to be encoded as a JSON string) with the data specified by the serialize_params option + // JSON.stringify() converts a primitive value, object or array to a JSON-formatted string that can later be parsed with JSON.parse(). + + var positions = JSON.stringify(this.serialize()); + + // With HTML5, web pages can store data locally within the user's browser. + // Earlier, this was done with cookies. However, Web Storage is more secure and faster. + // The data is not included with every server request, but used ONLY when asked for. + // It is also possible to store large amounts of data, without affecting the website's performance. + // The data is stored in key/value pairs, and a web page can only access data stored by itself. + + localStorage.setItem('positions', positions); + + + $.post( + "process.php", + {"positions": positions}, + function(data) + { + + // this is where you can check if your data is sent to the server or not. + // A status of 200 implies success + + console.log(data); + if(data==200) + console.log("Data successfully sent to the server"); + else + console.log + } + ); + + } + } + }).data('gridster'); + + + +}); + \ No newline at end of file diff --git a/GRID/assets/jquery.gridster.js b/GRID/assets/jquery.gridster.js new file mode 100644 index 0000000..947f4bb --- /dev/null +++ b/GRID/assets/jquery.gridster.js @@ -0,0 +1,3232 @@ +/*! gridster.js - v0.1.0 - 2012-10-20 +* http://gridster.net/ +* Copyright (c) 2012 ducksboard; Licensed MIT */ + +;(function($, window, document, undefined){ + /** + * Creates objects with coordinates (x1, y1, x2, y2, cx, cy, width, height) + * to simulate DOM elements on the screen. + * Coords is used by Gridster to create a faux grid with any DOM element can + * collide. + * + * @class Coords + * @param {HTMLElement|Object} obj The jQuery HTMLElement or a object with: left, + * top, width and height properties. + * @return {Object} Coords instance. + * @constructor + */ + function Coords(obj) { + if (obj[0] && $.isPlainObject(obj[0])) { + this.data = obj[0]; + }else { + this.el = obj; + } + + this.isCoords = true; + this.coords = {}; + this.init(); + return this; + } + + + var fn = Coords.prototype; + + + fn.init = function(){ + this.set(); + this.original_coords = this.get(); + }; + + + fn.set = function(update, not_update_offsets) { + var el = this.el; + + if (el && !update) { + this.data = el.offset(); + this.data.width = el.width(); + this.data.height = el.height(); + } + + if (el && update && !not_update_offsets) { + var offset = el.offset(); + this.data.top = offset.top; + this.data.left = offset.left; + } + + var d = this.data; + + this.coords.x1 = d.left; + this.coords.y1 = d.top; + this.coords.x2 = d.left + d.width; + this.coords.y2 = d.top + d.height; + this.coords.cx = d.left + (d.width / 2); + this.coords.cy = d.top + (d.height / 2); + this.coords.width = d.width; + this.coords.height = d.height; + this.coords.el = el || false ; + + return this; + }; + + + fn.update = function(data){ + if (!data && !this.el) { + return this; + } + + if (data) { + var new_data = $.extend({}, this.data, data); + this.data = new_data; + return this.set(true, true); + } + + this.set(true); + return this; + }; + + + fn.get = function(){ + return this.coords; + }; + + + //jQuery adapter + $.fn.coords = function() { + if (this.data('coords') ) { + return this.data('coords'); + } + + var ins = new Coords(this, arguments[0]); + this.data('coords', ins); + return ins; + }; + +}(jQuery, window, document)); + +;(function($, window, document, undefined){ + + var defaults = { + colliders_context: document.body + // ,on_overlap: function(collider_data){}, + // on_overlap_start : function(collider_data){}, + // on_overlap_stop : function(collider_data){} + }; + + + /** + * Detects collisions between a DOM element against other DOM elements or + * Coords objects. + * + * @class Collision + * @uses Coords + * @param {HTMLElement} el The jQuery wrapped HTMLElement. + * @param {HTMLElement|Array} colliders Can be a jQuery collection + * of HTMLElements or an Array of Coords instances. + * @param {Object} [options] An Object with all options you want to + * overwrite: + * @param {Function} [options.on_overlap_start] Executes a function the first + * time each `collider ` is overlapped. + * @param {Function} [options.on_overlap_stop] Executes a function when a + * `collider` is no longer collided. + * @param {Function} [options.on_overlap] Executes a function when the + * mouse is moved during the collision. + * @return {Object} Collision instance. + * @constructor + */ + function Collision(el, colliders, options) { + this.options = $.extend(defaults, options); + this.$element = el; + this.last_colliders = []; + this.last_colliders_coords = []; + if (typeof colliders === 'string' || colliders instanceof jQuery) { + this.$colliders = $(colliders, + this.options.colliders_context).not(this.$element); + }else{ + this.colliders = $(colliders); + } + + this.init(); + } + + + var fn = Collision.prototype; + + + fn.init = function() { + this.find_collisions(); + }; + + + fn.overlaps = function(a, b) { + var x = false; + var y = false; + + if ((b.x1 >= a.x1 && b.x1 <= a.x2) || + (b.x2 >= a.x1 && b.x2 <= a.x2) || + (a.x1 >= b.x1 && a.x2 <= b.x2) + ) { x = true; } + + if ((b.y1 >= a.y1 && b.y1 <= a.y2) || + (b.y2 >= a.y1 && b.y2 <= a.y2) || + (a.y1 >= b.y1 && a.y2 <= b.y2) + ) { y = true; } + + return (x && y); + }; + + + fn.detect_overlapping_region = function(a, b){ + var regionX = ''; + var regionY = ''; + + if (a.y1 > b.cy && a.y1 < b.y2) { regionX = 'N'; } + if (a.y2 > b.y1 && a.y2 < b.cy) { regionX = 'S'; } + if (a.x1 > b.cx && a.x1 < b.x2) { regionY = 'W'; } + if (a.x2 > b.x1 && a.x2 < b.cx) { regionY = 'E'; } + + return (regionX + regionY) || 'C'; + }; + + + fn.calculate_overlapped_area_coords = function(a, b){ + var x1 = Math.max(a.x1, b.x1); + var y1 = Math.max(a.y1, b.y1); + var x2 = Math.min(a.x2, b.x2); + var y2 = Math.min(a.y2, b.y2); + + return $({ + left: x1, + top: y1, + width : (x2 - x1), + height: (y2 - y1) + }).coords().get(); + }; + + + fn.calculate_overlapped_area = function(coords){ + return (coords.width * coords.height); + }; + + + fn.manage_colliders_start_stop = function(new_colliders_coords, start_callback, stop_callback){ + var last = this.last_colliders_coords; + + for (var i = 0, il = last.length; i < il; i++) { + if ($.inArray(last[i], new_colliders_coords) === -1) { + start_callback.call(this, last[i]); + } + } + + for (var j = 0, jl = new_colliders_coords.length; j < jl; j++) { + if ($.inArray(new_colliders_coords[j], last) === -1) { + stop_callback.call(this, new_colliders_coords[j]); + } + + } + }; + + + fn.find_collisions = function(player_data_coords){ + var self = this; + var colliders_coords = []; + var colliders_data = []; + var $colliders = (this.colliders || this.$colliders); + var count = $colliders.length; + var player_coords = self.$element.coords() + .update(player_data_coords || false).get(); + + while(count--){ + var $collider = self.$colliders ? + $($colliders[count]) : $colliders[count]; + var $collider_coords_ins = ($collider.isCoords) ? + $collider : $collider.coords(); + var collider_coords = $collider_coords_ins.get(); + var overlaps = self.overlaps(player_coords, collider_coords); + + if (!overlaps) { + continue; + } + + var region = self.detect_overlapping_region( + player_coords, collider_coords); + + //todo: make this an option + if (region === 'C'){ + var area_coords = self.calculate_overlapped_area_coords( + player_coords, collider_coords); + var area = self.calculate_overlapped_area(area_coords); + var collider_data = { + area: area, + area_coords : area_coords, + region: region, + coords: collider_coords, + player_coords: player_coords, + el: $collider + }; + + if (self.options.on_overlap) { + self.options.on_overlap.call(this, collider_data); + } + colliders_coords.push($collider_coords_ins); + colliders_data.push(collider_data); + } + } + + if (self.options.on_overlap_stop || self.options.on_overlap_start) { + this.manage_colliders_start_stop(colliders_coords, + self.options.on_overlap_stop, self.options.on_overlap_start); + } + + this.last_colliders_coords = colliders_coords; + + return colliders_data; + }; + + + fn.get_closest_colliders = function(player_data_coords){ + var colliders = this.find_collisions(player_data_coords); + + colliders.sort(function(a, b) { + /* if colliders are being overlapped by the "C" (center) region, + * we have to set a lower index in the array to which they are placed + * above in the grid. */ + if (a.region === 'C' && b.region === 'C') { + if (a.coords.y1 < b.coords.y1 || a.coords.x1 < b.coords.x1) { + return - 1; + }else{ + return 1; + } + } + + if (a.area < b.area) { + return 1; + } + + return 1; + }); + return colliders; + }; + + + //jQuery adapter + $.fn.collision = function(collider, options) { + return new Collision( this, collider, options ); + }; + + +}(jQuery, window, document)); + +;(function(window, undefined) { + /* Debounce and throttle functions taken from underscore.js */ + window.debounce = function(func, wait, immediate) { + var timeout; + return function() { + var context = this, args = arguments; + var later = function() { + timeout = null; + if (!immediate) func.apply(context, args); + }; + if (immediate && !timeout) func.apply(context, args); + clearTimeout(timeout); + timeout = setTimeout(later, wait); + }; + }; + + + window.throttle = function(func, wait) { + var context, args, timeout, throttling, more, result; + var whenDone = debounce( + function(){ more = throttling = false; }, wait); + return function() { + context = this; args = arguments; + var later = function() { + timeout = null; + if (more) func.apply(context, args); + whenDone(); + }; + if (!timeout) timeout = setTimeout(later, wait); + if (throttling) { + more = true; + } else { + result = func.apply(context, args); + } + whenDone(); + throttling = true; + return result; + }; + }; + +})(window); + +;(function($, window, document, undefined){ + + var defaults = { + items: '.gs_w', + distance: 1, + limit: true, + offset_left: 0, + autoscroll: true, + ignore_dragging: ['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON'], + handle: null + // ,drag: function(e){}, + // start : function(e, ui){}, + // stop : function(e){} + }; + + var $window = $(window); + var isTouch = !!('ontouchstart' in window); + var pointer_events = { + start: isTouch ? 'touchstart' : 'mousedown.draggable', + move: isTouch ? 'touchmove' : 'mousemove.draggable', + end: isTouch ? 'touchend' : 'mouseup.draggable' + }; + + /** + * Basic drag implementation for DOM elements inside a container. + * Provide start/stop/drag callbacks. + * + * @class Draggable + * @param {HTMLElement} el The HTMLelement that contains all the widgets + * to be dragged. + * @param {Object} [options] An Object with all options you want to + * overwrite: + * @param {HTMLElement|String} [options.items] Define who will + * be the draggable items. Can be a CSS Selector String or a + * collection of HTMLElements. + * @param {Number} [options.distance] Distance in pixels after mousedown + * the mouse must move before dragging should start. + * @param {Boolean} [options.limit] Constrains dragging to the width of + * the container + * @param {offset_left} [options.offset_left] Offset added to the item + * that is being dragged. + * @param {Number} [options.drag] Executes a callback when the mouse is + * moved during the dragging. + * @param {Number} [options.start] Executes a callback when the drag + * starts. + * @param {Number} [options.stop] Executes a callback when the drag stops. + * @return {Object} Returns `el`. + * @constructor + */ + function Draggable(el, options) { + this.options = $.extend({}, defaults, options); + this.$body = $(document.body); + this.$container = $(el); + this.$dragitems = $(this.options.items, this.$container); + this.is_dragging = false; + this.player_min_left = 0 + this.options.offset_left; + this.init(); + } + + var fn = Draggable.prototype; + + fn.init = function() { + this.calculate_positions(); + this.$container.css('position', 'relative'); + this.disabled = false; + this.events(); + + $(window).bind('resize', + throttle($.proxy(this.calculate_positions, this), 200)); + }; + + fn.events = function() { + this.$container.on('selectstart', $.proxy(this.on_select_start, this)); + + this.$container.on(pointer_events.start, this.options.items, $.proxy( + this.drag_handler, this)); + + this.$body.on(pointer_events.end, $.proxy(function(e) { + this.is_dragging = false; + if (this.disabled) { return; } + this.$body.off(pointer_events.move); + if (this.drag_start) { + this.on_dragstop(e); + } + }, this)); + }; + + fn.get_actual_pos = function($el) { + var pos = $el.position(); + return pos; + }; + + + fn.get_mouse_pos = function(e) { + if (isTouch) { + var oe = e.originalEvent; + e = oe.touches.length ? oe.touches[0] : oe.changedTouches[0]; + } + + return { + left: e.clientX, + top: e.clientY + }; + }; + + + fn.get_offset = function(e) { + e.preventDefault(); + var mouse_actual_pos = this.get_mouse_pos(e); + var diff_x = Math.round( + mouse_actual_pos.left - this.mouse_init_pos.left); + var diff_y = Math.round(mouse_actual_pos.top - this.mouse_init_pos.top); + + var left = Math.round(this.el_init_offset.left + diff_x - this.baseX); + var top = Math.round( + this.el_init_offset.top + diff_y - this.baseY + this.scrollOffset); + + if (this.options.limit) { + if (left > this.player_max_left) { + left = this.player_max_left; + }else if(left < this.player_min_left) { + left = this.player_min_left; + } + } + + return { + left: left, + top: top, + mouse_left: mouse_actual_pos.left, + mouse_top: mouse_actual_pos.top + }; + }; + + + fn.manage_scroll = function(offset) { + /* scroll document */ + var nextScrollTop; + var scrollTop = $window.scrollTop(); + var min_window_y = scrollTop; + var max_window_y = min_window_y + this.window_height; + + var mouse_down_zone = max_window_y - 50; + var mouse_up_zone = min_window_y + 50; + + var abs_mouse_left = offset.mouse_left; + var abs_mouse_top = min_window_y + offset.mouse_top; + + var max_player_y = (this.doc_height - this.window_height + + this.player_height); + + if (abs_mouse_top >= mouse_down_zone) { + nextScrollTop = scrollTop + 30; + if (nextScrollTop < max_player_y) { + $window.scrollTop(nextScrollTop); + this.scrollOffset = this.scrollOffset + 30; + } + } + + if (abs_mouse_top <= mouse_up_zone) { + nextScrollTop = scrollTop - 30; + if (nextScrollTop > 0) { + $window.scrollTop(nextScrollTop); + this.scrollOffset = this.scrollOffset - 30; + } + } + }; + + + fn.calculate_positions = function(e) { + this.window_height = $window.height(); + }; + + + fn.drag_handler = function(e) { + var node = e.target.nodeName; + if (this.disabled || e.which !== 1 && !isTouch) { + return; + } + + if (this.ignore_drag(e)) { + return; + } + + var self = this; + var first = true; + this.$player = $(e.currentTarget); + + this.el_init_pos = this.get_actual_pos(this.$player); + this.mouse_init_pos = this.get_mouse_pos(e); + this.offsetY = this.mouse_init_pos.top - this.el_init_pos.top; + + this.$body.on(pointer_events.move, function(mme){ + var mouse_actual_pos = self.get_mouse_pos(mme); + var diff_x = Math.abs( + mouse_actual_pos.left - self.mouse_init_pos.left); + var diff_y = Math.abs( + mouse_actual_pos.top - self.mouse_init_pos.top); + if (!(diff_x > self.options.distance || + diff_y > self.options.distance) + ) { + return false; + } + + if (first) { + first = false; + self.on_dragstart.call(self, mme); + return false; + } + + if (self.is_dragging === true) { + self.on_dragmove.call(self, mme); + } + + return false; + }); + + return false; + }; + + + fn.on_dragstart = function(e) { + e.preventDefault(); + this.drag_start = true; + this.is_dragging = true; + var offset = this.$container.offset(); + this.baseX = Math.round(offset.left); + this.baseY = Math.round(offset.top); + this.doc_height = $(document).height(); + + if (this.options.helper === 'clone') { + this.$helper = this.$player.clone() + .appendTo(this.$container).addClass('helper'); + this.helper = true; + }else{ + this.helper = false; + } + this.scrollOffset = 0; + this.el_init_offset = this.$player.offset(); + this.player_width = this.$player.width(); + this.player_height = this.$player.height(); + this.player_max_left = (this.$container.width() - this.player_width + + this.options.offset_left); + + if (this.options.start) { + this.options.start.call(this.$player, e, { + helper: this.helper ? this.$helper : this.$player + }); + } + return false; + }; + + + fn.on_dragmove = function(e) { + var offset = this.get_offset(e); + + this.options.autoscroll && this.manage_scroll(offset); + + (this.helper ? this.$helper : this.$player).css({ + 'position': 'absolute', + 'left' : offset.left, + 'top' : offset.top + }); + + var ui = { + 'position': { + 'left': offset.left, + 'top': offset.top + } + }; + + if (this.options.drag) { + this.options.drag.call(this.$player, e, ui); + } + return false; + }; + + + fn.on_dragstop = function(e) { + var offset = this.get_offset(e); + this.drag_start = false; + + var ui = { + 'position': { + 'left': offset.left, + 'top': offset.top + } + }; + + if (this.options.stop) { + this.options.stop.call(this.$player, e, ui); + } + + if (this.helper) { + this.$helper.remove(); + } + + return false; + }; + + fn.on_select_start = function(e) { + if (this.disabled) { return; } + + if (this.ignore_drag(e)) { + return; + } + + return false; + }; + + fn.enable = function() { + this.disabled = false; + }; + + fn.disable = function() { + this.disabled = true; + }; + + + fn.destroy = function(){ + this.disable(); + $.removeData(this.$container, 'drag'); + }; + + fn.ignore_drag = function(event) { + if (this.options.handle) { + return !$(event.target).is(this.options.handle); + } + + return $.inArray(event.target.nodeName, this.options.ignore_dragging) >= 0; + }; + + //jQuery adapter + $.fn.drag = function ( options ) { + return this.each(function () { + if (!$.data(this, 'drag')) { + $.data(this, 'drag', new Draggable( this, options )); + } + }); + }; + + +}(jQuery, window, document)); + +;(function($, window, document, undefined) { + + var defaults = { + namespace: '', + widget_selector: 'li', + widget_margins: [10, 10], + widget_base_dimensions: [400, 225], + extra_rows: 0, + extra_cols: 0, + min_cols: 1, + min_rows: 15, + max_size_x: 6, + autogenerate_stylesheet: true, + avoid_overlapped_widgets: true, + serialize_params: function($w, wgd) { + return { + col: wgd.col, + row: wgd.row, + size_x: wgd.size_x, + size_y: wgd.size_y + }; + }, + collision: {}, + draggable: { + distance: 4 + } + }; + + + /** + * @class Gridster + * @uses Draggable + * @uses Collision + * @param {HTMLElement} el The HTMLelement that contains all the widgets. + * @param {Object} [options] An Object with all options you want to + * overwrite: + * @param {HTMLElement|String} [options.widget_selector] Define who will + * be the draggable widgets. Can be a CSS Selector String or a + * collection of HTMLElements + * @param {Array} [options.widget_margins] Margin between widgets. + * The first index for the horizontal margin (left, right) and + * the second for the vertical margin (top, bottom). + * @param {Array} [options.widget_base_dimensions] Base widget dimensions + * in pixels. The first index for the width and the second for the + * height. + * @param {Number} [options.extra_cols] Add more columns in addition to + * those that have been calculated. + * @param {Number} [options.extra_rows] Add more rows in addition to + * those that have been calculated. + * @param {Number} [options.min_cols] The minimum required columns. + * @param {Number} [options.min_rows] The minimum required rows. + * @param {Number} [options.max_size_x] The maximum number of columns + * that a widget can span. + * @param {Boolean} [options.autogenerate_stylesheet] If true, all the + * CSS required to position all widgets in their respective columns + * and rows will be generated automatically and injected to the + * `` of the document. You can set this to false, and write + * your own CSS targeting rows and cols via data-attributes like so: + * `[data-col="1"] { left: 10px; }` + * @param {Boolean} [options.avoid_overlapped_widgets] Avoid that widgets loaded + * from the DOM can be overlapped. It is helpful if the positions were + * bad stored in the database or if there was any conflict. + * @param {Function} [options.serialize_params] Return the data you want + * for each widget in the serialization. Two arguments are passed: + * `$w`: the jQuery wrapped HTMLElement, and `wgd`: the grid + * coords object (`col`, `row`, `size_x`, `size_y`). + * @param {Object} [options.collision] An Object with all options for + * Collision class you want to overwrite. See Collision docs for + * more info. + * @param {Object} [options.draggable] An Object with all options for + * Draggable class you want to overwrite. See Draggable docs for more + * info. + * + * @constructor + */ + function Gridster(el, options) { + this.options = $.extend(true, defaults, options); + this.$el = $(el); + this.$wrapper = this.$el.parent(); + this.$widgets = this.$el.children(this.options.widget_selector).addClass('gs_w'); + this.widgets = []; + this.$changed = $([]); + this.wrapper_width = this.$wrapper.width(); + this.min_widget_width = (this.options.widget_margins[0] * 2) + + this.options.widget_base_dimensions[0]; + this.min_widget_height = (this.options.widget_margins[1] * 2) + + this.options.widget_base_dimensions[1]; + this.init(); + } + + Gridster.generated_stylesheets = []; + + var fn = Gridster.prototype; + + fn.init = function() { + this.generate_grid_and_stylesheet(); + this.get_widgets_from_DOM(); + this.set_dom_grid_height(); + this.$wrapper.addClass('ready'); + this.draggable(); + + $(window).bind( + 'resize', throttle($.proxy(this.recalculate_faux_grid, this), 200)); + }; + + + /** + * Disables dragging. + * + * @method disable + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.disable = function() { + this.$wrapper.find('.player-revert').removeClass('player-revert'); + this.drag_api.disable(); + return this; + }; + + + /** + * Enables dragging. + * + * @method enable + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.enable = function() { + this.drag_api.enable(); + return this; + }; + + + /** + * Add a new widget to the grid. + * + * @method add_widget + * @param {String|HTMLElement} html The string representing the HTML of the widget + * or the HTMLElement. + * @param {Number} [size_x] The nº of rows the widget occupies horizontally. + * @param {Number} [size_y] The nº of columns the widget occupies vertically. + * @param {Number} [col] The column the widget should start in. + * @param {Number} [row] The row the widget should start in. + * @return {HTMLElement} Returns the jQuery wrapped HTMLElement representing. + * the widget that was just created. + */ + fn.add_widget = function(html, size_x, size_y, col, row) { + var pos; + size_x || (size_x = 1); + size_y || (size_y = 1); + + if (!col & !row) { + pos = this.next_position(size_x, size_y); + }else{ + pos = { + col: col, + row: row + }; + + this.empty_cells(col, row, size_x, size_y); + } + + var $w = $(html).attr({ + 'data-col': pos.col, + 'data-row': pos.row, + 'data-sizex' : size_x, + 'data-sizey' : size_y + }).addClass('gs_w').appendTo(this.$el).hide(); + + this.$widgets = this.$widgets.add($w); + + this.register_widget($w); + + this.add_faux_rows(pos.size_y); + //this.add_faux_cols(pos.size_x); + + this.set_dom_grid_height(); + + return $w.fadeIn(); + }; + + + + /** + * Change the size of a widget. + * + * @method resize_widget + * @param {HTMLElement} $widget The jQuery wrapped HTMLElement + * representing the widget. + * @param {Number} size_x The number of columns that will occupy the widget. + * @param {Number} size_y The number of rows that will occupy the widget. + * @return {HTMLElement} Returns $widget. + */ + fn.resize_widget = function($widget, size_x, size_y) { + var wgd = $widget.coords().grid; + size_x || (size_x = wgd.size_x); + size_y || (size_y = wgd.size_y); + + if (size_x > this.cols) { + size_x = this.cols; + } + + var old_cells_occupied = this.get_cells_occupied(wgd); + var old_size_x = wgd.size_x; + var old_size_y = wgd.size_y; + var old_col = wgd.col; + var new_col = old_col; + var wider = size_x > old_size_x; + var taller = size_y > old_size_y; + + if (old_col + size_x - 1 > this.cols) { + var diff = old_col + (size_x - 1) - this.cols; + var c = old_col - diff; + new_col = Math.max(1, c); + } + + var new_grid_data = { + col: new_col, + row: wgd.row, + size_x: size_x, + size_y: size_y + }; + + var new_cells_occupied = this.get_cells_occupied(new_grid_data); + + var empty_cols = []; + $.each(old_cells_occupied.cols, function(i, col) { + if ($.inArray(col, new_cells_occupied.cols) === -1) { + empty_cols.push(col); + } + }); + + var occupied_cols = []; + $.each(new_cells_occupied.cols, function(i, col) { + if ($.inArray(col, old_cells_occupied.cols) === -1) { + occupied_cols.push(col); + } + }); + + var empty_rows = []; + $.each(old_cells_occupied.rows, function(i, row) { + if ($.inArray(row, new_cells_occupied.rows) === -1) { + empty_rows.push(row); + } + }); + + var occupied_rows = []; + $.each(new_cells_occupied.rows, function(i, row) { + if ($.inArray(row, old_cells_occupied.rows) === -1) { + occupied_rows.push(row); + } + }); + + this.remove_from_gridmap(wgd); + + if (occupied_cols.length) { + var cols_to_empty = [ + new_col, wgd.row, size_x, Math.min(old_size_y, size_y), $widget + ]; + this.empty_cells.apply(this, cols_to_empty); + } + + if (occupied_rows.length) { + var rows_to_empty = [new_col, wgd.row, size_x, size_y, $widget]; + this.empty_cells.apply(this, rows_to_empty); + } + + wgd.col = new_col; + wgd.size_x = size_x; + wgd.size_y = size_y; + this.add_to_gridmap(new_grid_data, $widget); + + //update coords instance attributes + $widget.data('coords').update({ + width: (size_x * this.options.widget_base_dimensions[0] + + ((size_x - 1) * this.options.widget_margins[0]) * 2), + height: (size_y * this.options.widget_base_dimensions[1] + + ((size_y - 1) * this.options.widget_margins[1]) * 2) + }); + + if (size_y > old_size_y) { + this.add_faux_rows(size_y - old_size_y); + } + + if (size_x > old_size_x) { + this.add_faux_cols(size_x - old_size_x); + } + + $widget.attr({ + 'data-col': new_col, + 'data-sizex': size_x, + 'data-sizey': size_y + }); + + if (empty_cols.length) { + var cols_to_remove_holes = [ + empty_cols[0], wgd.row, + empty_cols.length, + Math.min(old_size_y, size_y), + $widget + ]; + + this.remove_empty_cells.apply(this, cols_to_remove_holes); + } + + if (empty_rows.length) { + var rows_to_remove_holes = [ + new_col, wgd.row, size_x, size_y, $widget + ]; + this.remove_empty_cells.apply(this, rows_to_remove_holes); + } + + return $widget; + }; + + /** + * Move down widgets in cells represented by the arguments col, row, size_x, + * size_y + * + * @method empty_cells + * @param {Number} col The column where the group of cells begin. + * @param {Number} row The row where the group of cells begin. + * @param {Number} size_x The number of columns that the group of cells + * occupy. + * @param {Number} size_y The number of rows that the group of cells + * occupy. + * @param {HTMLElement} $exclude Exclude widgets from being moved. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.empty_cells = function(col, row, size_x, size_y, $exclude) { + var $nexts = this.widgets_below({ + col: col, + row: row - size_y, + size_x: size_x, + size_y: size_y + }); + + $nexts.not($exclude).each($.proxy(function(i, w) { + var wgd = $(w).coords().grid; + if (!(wgd.row <= (row + size_y - 1))) { return; } + var diff = (row + size_y) - wgd.row; + this.move_widget_down($(w), diff); + }, this)); + + this.set_dom_grid_height(); + + return this; + }; + + + /** + * Move up widgets below cells represented by the arguments col, row, size_x, + * size_y. + * + * @method remove_empty_cells + * @param {Number} col The column where the group of cells begin. + * @param {Number} row The row where the group of cells begin. + * @param {Number} size_x The number of columns that the group of cells + * occupy. + * @param {Number} size_y The number of rows that the group of cells + * occupy. + * @param {HTMLElement} $exclude Exclude widgets from being moved. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.remove_empty_cells = function(col, row, size_x, size_y, exclude) { + var $nexts = this.widgets_below({ + col: col, + row: row, + size_x: size_x, + size_y: size_y + }); + + $nexts.not(exclude).each($.proxy(function(i, widget) { + this.move_widget_up( $(widget), size_y ); + }, this)); + + this.set_dom_grid_height(); + + return this; + }; + + + /** + * Get the most left column below to add a new widget. + * + * @method next_position + * @param {Number} size_x The nº of rows the widget occupies horizontally. + * @param {Number} size_y The nº of columns the widget occupies vertically. + * @return {Object} Returns a grid coords object representing the future + * widget coords. + */ + fn.next_position = function(size_x, size_y) { + size_x || (size_x = 1); + size_y || (size_y = 1); + var ga = this.gridmap; + var cols_l = ga.length; + var valid_pos = []; + var rows_l; + + for (var c = 1; c < cols_l; c++) { + rows_l = ga[c].length; + for (var r = 1; r <= rows_l; r++) { + var can_move_to = this.can_move_to({ + size_x: size_x, + size_y: size_y + }, c, r); + + if (can_move_to) { + valid_pos.push({ + col: c, + row: r, + size_y: size_y, + size_x: size_x + }); + } + } + } + + if (valid_pos.length) { + return this.sort_by_row_and_col_asc(valid_pos)[0]; + } + return false; + }; + + + /** + * Remove a widget from the grid. + * + * @method remove_widget + * @param {HTMLElement} el The jQuery wrapped HTMLElement you want to remove. + * @param {Boolean|Function} silent If true, widgets below the removed one + * will not move up. If a Function is passed it will be used as callback. + * @param {Function} callback Function executed when the widget is removed. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.remove_widget = function(el, silent, callback) { + var $el = el instanceof jQuery ? el : $(el); + var wgd = $el.coords().grid; + + // if silent is a function assume it's a callback + if ($.isFunction(silent)) { + callback = silent; + silent = false; + } + + this.cells_occupied_by_placeholder = {}; + this.$widgets = this.$widgets.not($el); + + var $nexts = this.widgets_below($el); + + this.remove_from_gridmap(wgd); + + $el.fadeOut($.proxy(function() { + $el.remove(); + + if (!silent) { + $nexts.each($.proxy(function(i, widget) { + this.move_widget_up( $(widget), wgd.size_y ); + }, this)); + } + + this.set_dom_grid_height(); + + if (callback) { + callback.call(this, el); + } + }, this)); + }; + + + /** + * Remove all widgets from the grid. + * + * @method remove_all_widgets + * @param {Function} callback Function executed for each widget removed. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.remove_all_widgets = function(callback) { + this.$widgets.each($.proxy(function(i, el){ + this.remove_widget(el, true, callback); + }, this)); + + return this; + }; + + + /** + * Returns a serialized array of the widgets in the grid. + * + * @method serialize + * @param {HTMLElement} [$widgets] The collection of jQuery wrapped + * HTMLElements you want to serialize. If no argument is passed all widgets + * will be serialized. + * @return {Array} Returns an Array of Objects with the data specified in + * the serialize_params option. + */ + fn.serialize = function($widgets) { + $widgets || ($widgets = this.$widgets); + var result = []; + $widgets.each($.proxy(function(i, widget) { + result.push(this.options.serialize_params( + $(widget), $(widget).coords().grid ) ); + }, this)); + + return result; + }; + + + /** + * Returns a serialized array of the widgets that have changed their + * position. + * + * @method serialize_changed + * @return {Array} Returns an Array of Objects with the data specified in + * the serialize_params option. + */ + fn.serialize_changed = function() { + return this.serialize(this.$changed); + }; + + + /** + * Creates the grid coords object representing the widget a add it to the + * mapped array of positions. + * + * @method register_widget + * @return {Array} Returns the instance of the Gridster class. + */ + fn.register_widget = function($el) { + + var wgd = { + 'col': parseInt($el.attr('data-col'), 10), + 'row': parseInt($el.attr('data-row'), 10), + 'size_x': parseInt($el.attr('data-sizex'), 10), + 'size_y': parseInt($el.attr('data-sizey'), 10), + 'el': $el + }; + + if (this.options.avoid_overlapped_widgets && + !this.can_move_to( + {size_x: wgd.size_x, size_y: wgd.size_y}, wgd.col, wgd.row) + ) { + wgd = this.next_position(wgd.size_x, wgd.size_y); + wgd.el = $el; + $el.attr({ + 'data-col': wgd.col, + 'data-row': wgd.row, + 'data-sizex': wgd.size_x, + 'data-sizey': wgd.size_y + }); + } + + // attach Coord object to player data-coord attribute + $el.data('coords', $el.coords()); + + // Extend Coord object with grid position info + $el.data('coords').grid = wgd; + + this.add_to_gridmap(wgd, $el); + + return this; + }; + + + /** + * Update in the mapped array of positions the value of cells represented by + * the grid coords object passed in the `grid_data` param. + * + * @param {Object} grid_data The grid coords object representing the cells + * to update in the mapped array. + * @param {HTMLElement|Boolean} value Pass `false` or the jQuery wrapped + * HTMLElement, depends if you want to delete an existing position or add + * a new one. + * @method update_widget_position + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.update_widget_position = function(grid_data, value) { + this.for_each_cell_occupied(grid_data, function(col, row) { + if (!this.gridmap[col]) { return this; } + this.gridmap[col][row] = value; + }); + return this; + }; + + + /** + * Remove a widget from the mapped array of positions. + * + * @method remove_from_gridmap + * @param {Object} grid_data The grid coords object representing the cells + * to update in the mapped array. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.remove_from_gridmap = function(grid_data) { + return this.update_widget_position(grid_data, false); + }; + + + /** + * Add a widget to the mapped array of positions. + * + * @method add_to_gridmap + * @param {Object} grid_data The grid coords object representing the cells + * to update in the mapped array. + * @param {HTMLElement|Boolean} value The value to set in the specified + * position . + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.add_to_gridmap = function(grid_data, value) { + this.update_widget_position(grid_data, value || grid_data.el); + + if (grid_data.el) { + var $widgets = this.widgets_below(grid_data.el); + $widgets.each($.proxy(function(i, widget) { + this.move_widget_up( $(widget)); + }, this)); + } + }; + + + /** + * Make widgets draggable. + * + * @uses Draggable + * @method draggable + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.draggable = function() { + var self = this; + var draggable_options = $.extend(true, {}, this.options.draggable, { + offset_left: this.options.widget_margins[0], + start: function(event, ui) { + self.$widgets.filter('.player-revert') + .removeClass('player-revert'); + + self.$player = $(this); + self.$helper = self.options.draggable.helper === 'clone' ? + $(ui.helper) : self.$player; + self.helper = !self.$helper.is(self.$player); + + self.on_start_drag.call(self, event, ui); + self.$el.trigger('gridster:dragstart'); + }, + stop: function(event, ui) { + self.on_stop_drag.call(self, event, ui); + self.$el.trigger('gridster:dragstop'); + }, + drag: throttle(function(event, ui) { + self.on_drag.call(self, event, ui); + self.$el.trigger('gridster:drag'); + }, 60) + }); + + this.drag_api = this.$el.drag(draggable_options).data('drag'); + return this; + }; + + + /** + * This function is executed when the player begins to be dragged. + * + * @method on_start_drag + * @param {Event} The original browser event + * @param {Object} A prepared ui object. + */ + fn.on_start_drag = function(event, ui) { + + this.$helper.add(this.$player).add(this.$wrapper).addClass('dragging'); + + this.$player.addClass('player'); + this.player_grid_data = this.$player.coords().grid; + this.placeholder_grid_data = $.extend({}, this.player_grid_data); + + //set new grid height along the dragging period + this.$el.css('height', this.$el.height() + + (this.player_grid_data.size_y * this.min_widget_height)); + + var colliders = this.faux_grid; + var coords = this.$player.data('coords').coords; + + this.cells_occupied_by_player = this.get_cells_occupied( + this.player_grid_data); + this.cells_occupied_by_placeholder = this.get_cells_occupied( + this.placeholder_grid_data); + + this.last_cols = []; + this.last_rows = []; + + + // see jquery.collision.js + this.collision_api = this.$helper.collision( + colliders, this.options.collision); + + this.$preview_holder = $('
  • ', { + 'class': 'preview-holder', + 'data-row': this.$player.attr('data-row'), + 'data-col': this.$player.attr('data-col'), + css: { + width: coords.width, + height: coords.height + } + }).appendTo(this.$el); + + if (this.options.draggable.start) { + this.options.draggable.start.call(this, event, ui); + } + }; + + + /** + * This function is executed when the player is being dragged. + * + * @method on_drag + * @param {Event} The original browser event + * @param {Object} A prepared ui object. + */ + fn.on_drag = function(event, ui) { + //break if dragstop has been fired + if (this.$player === null) { + return false; + } + + var abs_offset = { + left: ui.position.left + this.baseX, + top: ui.position.top + this.baseY + }; + + this.colliders_data = this.collision_api.get_closest_colliders( + abs_offset); + + this.on_overlapped_column_change( + this.on_start_overlapping_column, + this.on_stop_overlapping_column + ); + + this.on_overlapped_row_change( + this.on_start_overlapping_row, + this.on_stop_overlapping_row + ); + + if (this.helper && this.$player) { + this.$player.css({ + 'left': ui.position.left, + 'top': ui.position.top + }); + } + + if (this.options.draggable.drag) { + this.options.draggable.drag.call(this, event, ui); + } + }; + + /** + * This function is executed when the player stops being dragged. + * + * @method on_stop_drag + * @param {Event} The original browser event + * @param {Object} A prepared ui object. + */ + fn.on_stop_drag = function(event, ui) { + this.$helper.add(this.$player).add(this.$wrapper) + .removeClass('dragging'); + + ui.position.left = ui.position.left + this.baseX; + ui.position.top = ui.position.top + this.baseY; + this.colliders_data = this.collision_api.get_closest_colliders(ui.position); + + this.on_overlapped_column_change( + this.on_start_overlapping_column, + this.on_stop_overlapping_column + ); + + this.on_overlapped_row_change( + this.on_start_overlapping_row, + this.on_stop_overlapping_row + ); + + this.$player.addClass('player-revert').removeClass('player') + .attr({ + 'data-col': this.placeholder_grid_data.col, + 'data-row': this.placeholder_grid_data.row + }).css({ + 'left': '', + 'top': '' + }); + + this.$changed = this.$changed.add(this.$player); + + this.cells_occupied_by_player = this.get_cells_occupied( + this.placeholder_grid_data); + this.set_cells_player_occupies( + this.placeholder_grid_data.col, this.placeholder_grid_data.row); + + this.$player.coords().grid.row = this.placeholder_grid_data.row; + this.$player.coords().grid.col = this.placeholder_grid_data.col; + + if (this.options.draggable.stop) { + this.options.draggable.stop.call(this, event, ui); + } + + this.$preview_holder.remove(); + + this.$player = null; + this.$helper = null; + this.placeholder_grid_data = {}; + this.player_grid_data = {}; + this.cells_occupied_by_placeholder = {}; + this.cells_occupied_by_player = {}; + + this.set_dom_grid_height(); + }; + + + /** + * Executes the callbacks passed as arguments when a column begins to be + * overlapped or stops being overlapped. + * + * @param {Function} start_callback Function executed when a new column + * begins to be overlapped. The column is passed as first argument. + * @param {Function} stop_callback Function executed when a column stops + * being overlapped. The column is passed as first argument. + * @method on_overlapped_column_change + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.on_overlapped_column_change = function(start_callback, stop_callback) { + if (!this.colliders_data.length) { + return; + } + var cols = this.get_targeted_columns( + this.colliders_data[0].el.data.col); + + var last_n_cols = this.last_cols.length; + var n_cols = cols.length; + var i; + + for (i = 0; i < n_cols; i++) { + if ($.inArray(cols[i], this.last_cols) === -1) { + (start_callback || $.noop).call(this, cols[i]); + } + } + + for (i = 0; i< last_n_cols; i++) { + if ($.inArray(this.last_cols[i], cols) === -1) { + (stop_callback || $.noop).call(this, this.last_cols[i]); + } + } + + this.last_cols = cols; + + return this; + }; + + + /** + * Executes the callbacks passed as arguments when a row starts to be + * overlapped or stops being overlapped. + * + * @param {Function} start_callback Function executed when a new row begins + * to be overlapped. The row is passed as first argument. + * @param {Function} stop_callback Function executed when a row stops being + * overlapped. The row is passed as first argument. + * @method on_overlapped_row_change + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.on_overlapped_row_change = function(start_callback, end_callback) { + if (!this.colliders_data.length) { + return; + } + var rows = this.get_targeted_rows(this.colliders_data[0].el.data.row); + var last_n_rows = this.last_rows.length; + var n_rows = rows.length; + var i; + + for (i = 0; i < n_rows; i++) { + if ($.inArray(rows[i], this.last_rows) === -1) { + (start_callback || $.noop).call(this, rows[i]); + } + } + + for (i = 0; i < last_n_rows; i++) { + if ($.inArray(this.last_rows[i], rows) === -1) { + (end_callback || $.noop).call(this, this.last_rows[i]); + } + } + + this.last_rows = rows; + }; + + + /** + * Sets the current position of the player + * + * @param {Function} start_callback Function executed when a new row begins + * to be overlapped. The row is passed as first argument. + * @param {Function} stop_callback Function executed when a row stops being + * overlapped. The row is passed as first argument. + * @method set_player + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.set_player = function(col, row, no_player) { + var self = this; + if (!no_player) { + this.empty_cells_player_occupies(); + } + var cell = !no_player ? self.colliders_data[0].el.data : {col: col}; + var to_col = cell.col; + var to_row = row || cell.row; + + this.player_grid_data = { + col: to_col, + row: to_row, + size_y : this.player_grid_data.size_y, + size_x : this.player_grid_data.size_x + }; + + this.cells_occupied_by_player = this.get_cells_occupied( + this.player_grid_data); + + var $overlapped_widgets = this.get_widgets_overlapped( + this.player_grid_data); + + var constraints = this.widgets_constraints($overlapped_widgets); + + this.manage_movements(constraints.can_go_up, to_col, to_row); + this.manage_movements(constraints.can_not_go_up, to_col, to_row); + + /* if there is not widgets overlapping in the new player position, + * update the new placeholder position. */ + if (!$overlapped_widgets.length) { + var pp = this.can_go_player_up(this.player_grid_data); + if (pp !== false) { + to_row = pp; + } + this.set_placeholder(to_col, to_row); + } + + return { + col: to_col, + row: to_row + }; + }; + + + /** + * See which of the widgets in the $widgets param collection can go to + * a upper row and which not. + * + * @method widgets_contraints + * @param {HTMLElements} $widgets A jQuery wrapped collection of + * HTMLElements. + * @return {Array} Returns a literal Object with two keys: `can_go_up` & + * `can_not_go_up`. Each contains a set of HTMLElements. + */ + fn.widgets_constraints = function($widgets) { + var $widgets_can_go_up = $([]); + var $widgets_can_not_go_up; + var wgd_can_go_up = []; + var wgd_can_not_go_up = []; + + $widgets.each($.proxy(function(i, w) { + var $w = $(w); + var wgd = $w.coords().grid; + if (this.can_go_widget_up(wgd)) { + $widgets_can_go_up = $widgets_can_go_up.add($w); + wgd_can_go_up.push(wgd); + }else{ + wgd_can_not_go_up.push(wgd); + } + }, this)); + + $widgets_can_not_go_up = $widgets.not($widgets_can_go_up); + + return { + can_go_up: this.sort_by_row_asc(wgd_can_go_up), + can_not_go_up: this.sort_by_row_desc(wgd_can_not_go_up) + }; + }; + + + /** + * Sorts an Array of grid coords objects (representing the grid coords of + * each widget) in ascending way. + * + * @method sort_by_row_asc + * @param {Array} widgets Array of grid coords objects + * @return {Array} Returns the array sorted. + */ + fn.sort_by_row_asc = function(widgets) { + widgets = widgets.sort(function(a, b) { + if (!a.row) { + a = $(a).coords().grid; + b = $(b).coords().grid; + } + + if (a.row > b.row) { + return 1; + } + return -1; + }); + + return widgets; + }; + + + /** + * Sorts an Array of grid coords objects (representing the grid coords of + * each widget) placing first the empty cells upper left. + * + * @method sort_by_row_and_col_asc + * @param {Array} widgets Array of grid coords objects + * @return {Array} Returns the array sorted. + */ + fn.sort_by_row_and_col_asc = function(widgets) { + widgets = widgets.sort(function(a, b) { + if (a.row > b.row || a.row === b.row && a.col > b.col) { + return 1; + } + return -1; + }); + + return widgets; + }; + + + /** + * Sorts an Array of grid coords objects by column (representing the grid + * coords of each widget) in ascending way. + * + * @method sort_by_col_asc + * @param {Array} widgets Array of grid coords objects + * @return {Array} Returns the array sorted. + */ + fn.sort_by_col_asc = function(widgets) { + widgets = widgets.sort(function(a, b) { + if (a.col > b.col) { + return 1; + } + return -1; + }); + + return widgets; + }; + + + /** + * Sorts an Array of grid coords objects (representing the grid coords of + * each widget) in descending way. + * + * @method sort_by_row_desc + * @param {Array} widgets Array of grid coords objects + * @return {Array} Returns the array sorted. + */ + fn.sort_by_row_desc = function(widgets) { + widgets = widgets.sort(function(a, b) { + if (a.row + a.size_y < b.row + b.size_y) { + return 1; + } + return -1; + }); + return widgets; + }; + + + /** + * Sorts an Array of grid coords objects (representing the grid coords of + * each widget) in descending way. + * + * @method manage_movements + * @param {HTMLElements} $widgets A jQuery collection of HTMLElements + * representing the widgets you want to move. + * @param {Number} to_col The column to which we want to move the widgets. + * @param {Number} to_row The row to which we want to move the widgets. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.manage_movements = function($widgets, to_col, to_row) { + $.each($widgets, $.proxy(function(i, w) { + var wgd = w; + var $w = wgd.el; + + var can_go_widget_up = this.can_go_widget_up(wgd); + + if (can_go_widget_up) { + //target CAN go up + //so move widget up + this.move_widget_to($w, can_go_widget_up); + this.set_placeholder(to_col, can_go_widget_up + wgd.size_y); + + } else { + //target can't go up + var can_go_player_up = this.can_go_player_up( + this.player_grid_data); + + if (!can_go_player_up) { + // target can't go up + // player cant't go up + // so we need to move widget down to a position that dont + // overlaps player + var y = (to_row + this.player_grid_data.size_y) - wgd.row; + + this.move_widget_down($w, y); + this.set_placeholder(to_col, to_row); + } + } + }, this)); + + return this; + }; + + /** + * Determines if there is a widget in the row and col given. Or if the + * HTMLElement passed as first argument is the player. + * + * @method is_player + * @param {Number|HTMLElement} col_or_el A jQuery wrapped collection of + * HTMLElements. + * @param {Number} [row] The column to which we want to move the widgets. + * @return {Boolean} Returns true or false. + */ + fn.is_player = function(col_or_el, row) { + if (row && !this.gridmap[col_or_el]) { return false; } + var $w = row ? this.gridmap[col_or_el][row] : col_or_el; + return $w && ($w.is(this.$player) || $w.is(this.$helper)); + }; + + + /** + * Determines if the widget that is being dragged is currently over the row + * and col given. + * + * @method is_player_in + * @param {Number} col The column to check. + * @param {Number} row The row to check. + * @return {Boolean} Returns true or false. + */ + fn.is_player_in = function(col, row) { + var c = this.cells_occupied_by_player || {}; + return $.inArray(col, c.cols) >= 0 && $.inArray(row, c.rows) >= 0; + }; + + + /** + * Determines if the placeholder is currently over the row and col given. + * + * @method is_placeholder_in + * @param {Number} col The column to check. + * @param {Number} row The row to check. + * @return {Boolean} Returns true or false. + */ + fn.is_placeholder_in = function(col, row) { + var c = this.cells_occupied_by_placeholder || {}; + return this.is_placeholder_in_col(col) && $.inArray(row, c.rows) >= 0; + }; + + + /** + * Determines if the placeholder is currently over the column given. + * + * @method is_placeholder_in_col + * @param {Number} col The column to check. + * @return {Boolean} Returns true or false. + */ + fn.is_placeholder_in_col = function(col) { + var c = this.cells_occupied_by_placeholder || []; + return $.inArray(col, c.cols) >= 0; + }; + + + /** + * Determines if the cell represented by col and row params is empty. + * + * @method is_empty + * @param {Number} col The column to check. + * @param {Number} row The row to check. + * @return {Boolean} Returns true or false. + */ + fn.is_empty = function(col, row) { + if (typeof this.gridmap[col] !== 'undefined' && + typeof this.gridmap[col][row] !== 'undefined' && + this.gridmap[col][row] === false + ) { + return true; + } + return false; + }; + + + /** + * Determines if the cell represented by col and row params is occupied. + * + * @method is_occupied + * @param {Number} col The column to check. + * @param {Number} row The row to check. + * @return {Boolean} Returns true or false. + */ + fn.is_occupied = function(col, row) { + if (!this.gridmap[col]) { + return false; + } + + if (this.gridmap[col][row]) { + return true; + } + return false; + }; + + + /** + * Determines if there is a widget in the cell represented by col/row params. + * + * @method is_widget + * @param {Number} col The column to check. + * @param {Number} row The row to check. + * @return {Boolean|HTMLElement} Returns false if there is no widget, + * else returns the jQuery HTMLElement + */ + fn.is_widget = function(col, row) { + var cell = this.gridmap[col]; + if (!cell) { + return false; + } + + cell = cell[row]; + + if (cell) { + return cell; + } + + return false; + }; + + + /** + * Determines if there is a widget in the cell represented by col/row + * params and if this is under the widget that is being dragged. + * + * @method is_widget_under_player + * @param {Number} col The column to check. + * @param {Number} row The row to check. + * @return {Boolean} Returns true or false. + */ + fn.is_widget_under_player = function(col, row) { + if (this.is_widget(col, row)) { + return this.is_player_in(col, row); + } + return false; + }; + + + /** + * Get widgets overlapping with the player or with the object passed + * representing the grid cells. + * + * @method get_widgets_under_player + * @return {HTMLElement} Returns a jQuery collection of HTMLElements + */ + fn.get_widgets_under_player = function(cells) { + cells || (cells = this.cells_occupied_by_player || {cols: [], rows: []}); + var $widgets = $([]); + + $.each(cells.cols, $.proxy(function(i, col) { + $.each(cells.rows, $.proxy(function(i, row) { + if(this.is_widget(col, row)) { + $widgets = $widgets.add(this.gridmap[col][row]); + } + }, this)); + }, this)); + + return $widgets; + }; + + + /** + * Put placeholder at the row and column specified. + * + * @method set_placeholder + * @param {Number} col The column to which we want to move the + * placeholder. + * @param {Number} row The row to which we want to move the + * placeholder. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.set_placeholder = function(col, row) { + var phgd = $.extend({}, this.placeholder_grid_data); + var $nexts = this.widgets_below({ + col: phgd.col, + row: phgd.row, + size_y: phgd.size_y, + size_x: phgd.size_x + }); + + // Prevents widgets go out of the grid + var right_col = (col + phgd.size_x - 1); + if (right_col > this.cols) { + col = col - (right_col - col); + } + + var moved_down = this.placeholder_grid_data.row < row; + var changed_column = this.placeholder_grid_data.col !== col; + + this.placeholder_grid_data.col = col; + this.placeholder_grid_data.row = row; + + this.cells_occupied_by_placeholder = this.get_cells_occupied( + this.placeholder_grid_data); + + this.$preview_holder.attr({ + 'data-row' : row, + 'data-col' : col + }); + + if (moved_down || changed_column) { + $nexts.each($.proxy(function(i, widget) { + this.move_widget_up( + $(widget), this.placeholder_grid_data.col - col + phgd.size_y); + }, this)); + } + + + var $widgets_under_ph = this.get_widgets_under_player(this.cells_occupied_by_placeholder); + if ($widgets_under_ph.length) { + $widgets_under_ph.each($.proxy(function(i, widget) { + var $w = $(widget); + this.move_widget_down( + $w, row + phgd.size_y - $w.data('coords').grid.row); + }, this)); + } + + }; + + + /** + * Determines whether the player can move to a position above. + * + * @method can_go_player_up + * @param {Object} widget_grid_data The actual grid coords object of the + * player. + * @return {Number|Boolean} If the player can be moved to an upper row + * returns the row number, else returns false. + */ + fn.can_go_player_up = function(widget_grid_data) { + var p_bottom_row = widget_grid_data.row + widget_grid_data.size_y - 1; + var result = true; + var upper_rows = []; + var min_row = 10000; + var $widgets_under_player = this.get_widgets_under_player(); + + /* generate an array with columns as index and array with upper rows + * empty as value */ + this.for_each_column_occupied(widget_grid_data, function(tcol) { + var grid_col = this.gridmap[tcol]; + var r = p_bottom_row + 1; + upper_rows[tcol] = []; + + while (--r > 0) { + if (this.is_empty(tcol, r) || this.is_player(tcol, r) || + this.is_widget(tcol, r) && + grid_col[r].is($widgets_under_player) + ) { + upper_rows[tcol].push(r); + min_row = r < min_row ? r : min_row; + }else{ + break; + } + } + + if (upper_rows[tcol].length === 0) { + result = false; + return true; //break + } + + upper_rows[tcol].sort(); + }); + + if (!result) { return false; } + + return this.get_valid_rows(widget_grid_data, upper_rows, min_row); + }; + + + /** + * Determines whether a widget can move to a position above. + * + * @method can_go_widget_up + * @param {Object} widget_grid_data The actual grid coords object of the + * widget we want to check. + * @return {Number|Boolean} If the widget can be moved to an upper row + * returns the row number, else returns false. + */ + fn.can_go_widget_up = function(widget_grid_data) { + var p_bottom_row = widget_grid_data.row + widget_grid_data.size_y - 1; + var result = true; + var upper_rows = []; + var min_row = 10000; + + /* generate an array with columns as index and array with topmost rows + * empty as value */ + this.for_each_column_occupied(widget_grid_data, function(tcol) { + var grid_col = this.gridmap[tcol]; + upper_rows[tcol] = []; + + var r = p_bottom_row + 1; + // iterate over each row + while (--r > 0) { + if (this.is_widget(tcol, r) && !this.is_player_in(tcol, r)) { + if (!grid_col[r].is(widget_grid_data.el)) { + break; + } + } + + if (!this.is_player(tcol, r) && + !this.is_placeholder_in(tcol, r) && + !this.is_player_in(tcol, r)) { + upper_rows[tcol].push(r); + } + + if (r < min_row) { + min_row = r; + } + } + + if (upper_rows[tcol].length === 0) { + result = false; + return true; //break + } + + upper_rows[tcol].sort(); + }); + + if (!result) { return false; } + + return this.get_valid_rows(widget_grid_data, upper_rows, min_row); + }; + + + /** + * Search a valid row for the widget represented by `widget_grid_data' in + * the `upper_rows` array. Iteration starts from row specified in `min_row`. + * + * @method get_valid_rows + * @param {Object} widget_grid_data The actual grid coords object of the + * player. + * @param {Array} upper_rows An array with columns as index and arrays + * of valid rows as values. + * @param {Number} min_row The upper row from which the iteration will start. + * @return {Number|Boolean} Returns the upper row valid from the `upper_rows` + * for the widget in question. + */ + fn.get_valid_rows = function(widget_grid_data, upper_rows, min_row) { + var p_top_row = widget_grid_data.row; + var p_bottom_row = widget_grid_data.row + widget_grid_data.size_y - 1; + var size_y = widget_grid_data.size_y; + var r = min_row - 1; + var valid_rows = []; + + while (++r <= p_bottom_row ) { + var common = true; + $.each(upper_rows, function(col, rows) { + if ($.isArray(rows) && $.inArray(r, rows) === -1) { + common = false; + } + }); + + if (common === true) { + valid_rows.push(r); + if (valid_rows.length === size_y) { + break; + } + } + } + + var new_row = false; + if (size_y === 1) { + if (valid_rows[0] !== p_top_row) { + new_row = valid_rows[0] || false; + } + }else{ + if (valid_rows[0] !== p_top_row) { + new_row = this.get_consecutive_numbers_index( + valid_rows, size_y); + } + } + + return new_row; + }; + + + fn.get_consecutive_numbers_index = function(arr, size_y) { + var max = arr.length; + var result = []; + var first = true; + var prev = -1; // or null? + + for (var i=0; i < max; i++) { + if (first || arr[i] === prev + 1) { + result.push(i); + if (result.length === size_y) { + break; + } + first = false; + }else{ + result = []; + first = true; + } + + prev = arr[i]; + } + + return result.length >= size_y ? arr[result[0]] : false; + }; + + + /** + * Get widgets overlapping with the player. + * + * @method get_widgets_overlapped + * @return {HTMLElements} Returns a jQuery collection of HTMLElements. + */ + fn.get_widgets_overlapped = function() { + var $w; + var $widgets = $([]); + var used = []; + var rows_from_bottom = this.cells_occupied_by_player.rows.slice(0); + rows_from_bottom.reverse(); + + $.each(this.cells_occupied_by_player.cols, $.proxy(function(i, col) { + $.each(rows_from_bottom, $.proxy(function(i, row) { + // if there is a widget in the player position + if (!this.gridmap[col]) { return true; } //next iteration + var $w = this.gridmap[col][row]; + if (this.is_occupied(col, row) && !this.is_player($w) && + $.inArray($w, used) === -1 + ) { + $widgets = $widgets.add($w); + used.push($w); + } + + }, this)); + }, this)); + + return $widgets; + }; + + + /** + * This callback is executed when the player begins to collide with a column. + * + * @method on_start_overlapping_column + * @param {Number} col The collided column. + * @return {HTMLElements} Returns a jQuery collection of HTMLElements. + */ + fn.on_start_overlapping_column = function(col) { + this.set_player(col, false); + }; + + + /** + * A callback executed when the player begins to collide with a row. + * + * @method on_start_overlapping_row + * @param {Number} col The collided row. + * @return {HTMLElements} Returns a jQuery collection of HTMLElements. + */ + fn.on_start_overlapping_row = function(row) { + this.set_player(false, row); + }; + + + /** + * A callback executed when the the player ends to collide with a column. + * + * @method on_stop_overlapping_column + * @param {Number} col The collided row. + * @return {HTMLElements} Returns a jQuery collection of HTMLElements. + */ + fn.on_stop_overlapping_column = function(col) { + this.set_player(col, false); + + var self = this; + this.for_each_widget_below(col, this.cells_occupied_by_player.rows[0], + function(tcol, trow) { + self.move_widget_up(this, self.player_grid_data.size_y); + }); + }; + + + /** + * This callback is executed when the player ends to collide with a row. + * + * @method on_stop_overlapping_row + * @param {Number} row The collided row. + * @return {HTMLElements} Returns a jQuery collection of HTMLElements. + */ + fn.on_stop_overlapping_row = function(row) { + this.set_player(false, row); + + var self = this; + var cols = this.cells_occupied_by_player.cols; + for (var c = 0, cl = cols.length; c < cl; c++) { + this.for_each_widget_below(cols[c], row, function(tcol, trow) { + self.move_widget_up(this, self.player_grid_data.size_y); + }); + } + }; + + + /** + * Move a widget to a specific row. The cell or cells must be empty. + * If the widget has widgets below, all of these widgets will be moved also + * if they can. + * + * @method move_widget_to + * @param {HTMLElement} $widget The jQuery wrapped HTMLElement of the + * widget is going to be moved. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.move_widget_to = function($widget, row) { + var self = this; + var widget_grid_data = $widget.coords().grid; + var diff = row - widget_grid_data.row; + var $next_widgets = this.widgets_below($widget); + + var can_move_to_new_cell = this.can_move_to( + widget_grid_data, widget_grid_data.col, row, $widget); + + if (can_move_to_new_cell === false) { + return false; + } + + this.remove_from_gridmap(widget_grid_data); + widget_grid_data.row = row; + this.add_to_gridmap(widget_grid_data); + $widget.attr('data-row', row); + this.$changed = this.$changed.add($widget); + + + $next_widgets.each(function(i, widget) { + var $w = $(widget); + var wgd = $w.coords().grid; + var can_go_up = self.can_go_widget_up(wgd); + if (can_go_up && can_go_up !== wgd.row) { + self.move_widget_to($w, can_go_up); + } + }); + + return this; + }; + + + /** + * Move up the specified widget and all below it. + * + * @method move_widget_up + * @param {HTMLElement} $widget The widget you want to move. + * @param {Number} [y_units] The number of cells that the widget has to move. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.move_widget_up = function($widget, y_units) { + var el_grid_data = $widget.coords().grid; + var actual_row = el_grid_data.row; + var moved = []; + var can_go_up = true; + y_units || (y_units = 1); + + if (!this.can_go_up($widget)) { return false; } //break; + + this.for_each_column_occupied(el_grid_data, function(col) { + // can_go_up + if ($.inArray($widget, moved) === -1) { + var widget_grid_data = $widget.coords().grid; + var next_row = actual_row - y_units; + next_row = this.can_go_up_to_row( + widget_grid_data, col, next_row); + + if (!next_row) { + return true; + } + + var $next_widgets = this.widgets_below($widget); + + this.remove_from_gridmap(widget_grid_data); + widget_grid_data.row = next_row; + this.add_to_gridmap(widget_grid_data); + $widget.attr('data-row', widget_grid_data.row); + this.$changed = this.$changed.add($widget); + + moved.push($widget); + + $next_widgets.each($.proxy(function(i, widget) { + this.move_widget_up($(widget), y_units); + }, this)); + } + }); + + }; + + + /** + * Move down the specified widget and all below it. + * + * @method move_widget_down + * @param {HTMLElement} $widget The jQuery object representing the widget + * you want to move. + * @param {Number} The number of cells that the widget has to move. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.move_widget_down = function($widget, y_units) { + var el_grid_data = $widget.coords().grid; + var actual_row = el_grid_data.row; + var moved = []; + var y_diff = y_units; + + if (!$widget) { return false; } + + if ($.inArray($widget, moved) === -1) { + + var widget_grid_data = $widget.coords().grid; + var next_row = actual_row + y_units; + var $next_widgets = this.widgets_below($widget); + + this.remove_from_gridmap(widget_grid_data); + + $next_widgets.each($.proxy(function(i, widget) { + var $w = $(widget); + var wd = $w.coords().grid; + var tmp_y = this.displacement_diff( + wd, widget_grid_data, y_diff); + + if (tmp_y > 0) { + this.move_widget_down($w, tmp_y); + } + }, this)); + + widget_grid_data.row = next_row; + this.update_widget_position(widget_grid_data, $widget); + $widget.attr('data-row', widget_grid_data.row); + this.$changed = this.$changed.add($widget); + + moved.push($widget); + } + }; + + + /** + * Check if the widget can move to the specified row, else returns the + * upper row possible. + * + * @method can_go_up_to_row + * @param {Number} widget_grid_data The current grid coords object of the + * widget. + * @param {Number} col The target column. + * @param {Number} row The target row. + * @return {Boolean|Number} Returns the row number if the widget can move + * to the target position, else returns false. + */ + fn.can_go_up_to_row = function(widget_grid_data, col, row) { + var ga = this.gridmap; + var result = true; + var urc = []; // upper_rows_in_columns + var actual_row = widget_grid_data.row; + var r; + + /* generate an array with columns as index and array with + * upper rows empty in the column */ + this.for_each_column_occupied(widget_grid_data, function(tcol) { + var grid_col = ga[tcol]; + urc[tcol] = []; + + r = actual_row; + while (r--) { + if (this.is_empty(tcol, r) && + !this.is_placeholder_in(tcol, r) + ) { + urc[tcol].push(r); + }else{ + break; + } + } + + if (!urc[tcol].length) { + result = false; + return true; + } + + }); + + if (!result) { return false; } + + /* get common rows starting from upper position in all the columns + * that widget occupies */ + r = row; + for (r = 1; r < actual_row; r++) { + var common = true; + + for (var uc = 0, ucl = urc.length; uc < ucl; uc++) { + if (urc[uc] && $.inArray(r, urc[uc]) === -1) { + common = false; + } + } + + if (common === true) { + result = r; + break; + } + } + + return result; + }; + + + fn.displacement_diff = function(widget_grid_data, parent_bgd, y_units) { + var actual_row = widget_grid_data.row; + var diffs = []; + var parent_max_y = parent_bgd.row + parent_bgd.size_y; + + this.for_each_column_occupied(widget_grid_data, function(col) { + var temp_y_units = 0; + + for (var r = parent_max_y; r < actual_row; r++) { + if (this.is_empty(col, r)) { + temp_y_units = temp_y_units + 1; + } + } + + diffs.push(temp_y_units); + }); + + var max_diff = Math.max.apply(Math, diffs); + y_units = (y_units - max_diff); + + return y_units > 0 ? y_units : 0; + }; + + + /** + * Get widgets below a widget. + * + * @method widgets_below + * @param {HTMLElement} $el The jQuery wrapped HTMLElement. + * @return {HTMLElements} A jQuery collection of HTMLElements. + */ + fn.widgets_below = function($el) { + var el_grid_data = $.isPlainObject($el) ? $el : $el.coords().grid; + var self = this; + var ga = this.gridmap; + var next_row = el_grid_data.row + el_grid_data.size_y - 1; + var $nexts = $([]); + + this.for_each_column_occupied(el_grid_data, function(col) { + self.for_each_widget_below(col, next_row, function(tcol, trow) { + if (!self.is_player(this) && $.inArray(this, $nexts) === -1) { + $nexts = $nexts.add(this); + return true; // break + } + }); + }); + + return this.sort_by_row_asc($nexts); + }; + + + /** + * Update the array of mapped positions with the new player position. + * + * @method set_cells_player_occupies + * @param {Number} col The new player col. + * @param {Number} col The new player row. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.set_cells_player_occupies = function(col, row) { + this.remove_from_gridmap(this.placeholder_grid_data); + this.placeholder_grid_data.col = col; + this.placeholder_grid_data.row = row; + this.add_to_gridmap(this.placeholder_grid_data, this.$player); + return this; + }; + + + /** + * Remove from the array of mapped positions the reference to the player. + * + * @method empty_cells_player_occupies + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.empty_cells_player_occupies = function() { + this.remove_from_gridmap(this.placeholder_grid_data); + return this; + }; + + + fn.can_go_up = function($el) { + var el_grid_data = $el.coords().grid; + var initial_row = el_grid_data.row; + var prev_row = initial_row - 1; + var ga = this.gridmap; + var upper_rows_by_column = []; + + var result = true; + if (initial_row === 1) { return false; } + + this.for_each_column_occupied(el_grid_data, function(col) { + var $w = this.is_widget(col, prev_row); + + if (this.is_occupied(col, prev_row) || + this.is_player(col, prev_row) || + this.is_placeholder_in(col, prev_row) || + this.is_player_in(col, prev_row) + ) { + result = false; + return true; //break + } + }); + + return result; + }; + + + + /** + * Check if it's possible to move a widget to a specific col/row. It takes + * into account the dimensions (`size_y` and `size_x` attrs. of the grid + * coords object) the widget occupies. + * + * @method can_move_to + * @param {Object} widget_grid_data The grid coords object that represents + * the widget. + * @param {Object} col The col to check. + * @param {Object} row The row to check. + * @param {Number} [max_row] The max row allowed. + * @return {Boolean} Returns true if all cells are empty, else return false. + */ + fn.can_move_to = function(widget_grid_data, col, row, max_row) { + var ga = this.gridmap; + var $w = widget_grid_data.el; + var future_wd = { + size_y: widget_grid_data.size_y, + size_x: widget_grid_data.size_x, + col: col, + row: row + }; + var result = true; + + //Prevents widgets go out of the grid + var right_col = col + widget_grid_data.size_x - 1; + if (right_col > this.cols) { + return false; + } + + if (max_row && max_row < row + widget_grid_data.size_y - 1) { + return false; + } + + this.for_each_cell_occupied(future_wd, function(tcol, trow) { + var $tw = this.is_widget(tcol, trow); + if ($tw && (!widget_grid_data.el || $tw.is($w))) { + result = false; + } + }); + + return result; + }; + + + /** + * Given the leftmost column returns all columns that are overlapping + * with the player. + * + * @method get_targeted_columns + * @param {Number} [from_col] The leftmost column. + * @return {Array} Returns an array with column numbers. + */ + fn.get_targeted_columns = function(from_col) { + var max = (from_col || this.player_grid_data.col) + + (this.player_grid_data.size_x - 1); + var cols = []; + for (var col = from_col; col <= max; col++) { + cols.push(col); + } + return cols; + }; + + + /** + * Given the upper row returns all rows that are overlapping with the player. + * + * @method get_targeted_rows + * @param {Number} [from_row] The upper row. + * @return {Array} Returns an array with row numbers. + */ + fn.get_targeted_rows = function(from_row) { + var max = (from_row || this.player_grid_data.row) + + (this.player_grid_data.size_y - 1); + var rows = []; + for (var row = from_row; row <= max; row++) { + rows.push(row); + } + return rows; + }; + + /** + * Get all columns and rows that a widget occupies. + * + * @method get_cells_occupied + * @param {Object} el_grid_data The grid coords object of the widget. + * @return {Object} Returns an object like `{ cols: [], rows: []}`. + */ + fn.get_cells_occupied = function(el_grid_data) { + var cells = { cols: [], rows: []}; + var i; + if (arguments[1] instanceof jQuery) { + el_grid_data = arguments[1].coords().grid; + } + + for (i = 0; i < el_grid_data.size_x; i++) { + var col = el_grid_data.col + i; + cells.cols.push(col); + } + + for (i = 0; i < el_grid_data.size_y; i++) { + var row = el_grid_data.row + i; + cells.rows.push(row); + } + + return cells; + }; + + + /** + * Iterate over the cells occupied by a widget executing a function for + * each one. + * + * @method for_each_cell_occupied + * @param {Object} el_grid_data The grid coords object that represents the + * widget. + * @param {Function} callback The function to execute on each column + * iteration. Column and row are passed as arguments. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.for_each_cell_occupied = function(grid_data, callback) { + this.for_each_column_occupied(grid_data, function(col) { + this.for_each_row_occupied(grid_data, function(row) { + callback.call(this, col, row); + }); + }); + return this; + }; + + + /** + * Iterate over the columns occupied by a widget executing a function for + * each one. + * + * @method for_each_column_occupied + * @param {Object} el_grid_data The grid coords object that represents + * the widget. + * @param {Function} callback The function to execute on each column + * iteration. The column number is passed as first argument. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.for_each_column_occupied = function(el_grid_data, callback) { + for (var i = 0; i < el_grid_data.size_x; i++) { + var col = el_grid_data.col + i; + callback.call(this, col, el_grid_data); + } + }; + + + /** + * Iterate over the rows occupied by a widget executing a function for + * each one. + * + * @method for_each_row_occupied + * @param {Object} el_grid_data The grid coords object that represents + * the widget. + * @param {Function} callback The function to execute on each column + * iteration. The row number is passed as first argument. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.for_each_row_occupied = function(el_grid_data, callback) { + for (var i = 0; i < el_grid_data.size_y; i++) { + var row = el_grid_data.row + i; + callback.call(this, row, el_grid_data); + } + }; + + + + fn._traversing_widgets = function(type, direction, col, row, callback) { + var ga = this.gridmap; + if (!ga[col]) { return; } + + var cr, max; + var action = type + '/' + direction; + if (arguments[2] instanceof jQuery) { + var el_grid_data = arguments[2].coords().grid; + col = el_grid_data.col; + row = el_grid_data.row; + callback = arguments[3]; + } + var matched = []; + var trow = row; + + + var methods = { + 'for_each/above': function() { + while (trow--) { + if (trow > 0 && this.is_widget(col, trow) && + $.inArray(ga[col][trow], matched) === -1 + ) { + cr = callback.call(ga[col][trow], col, trow); + matched.push(ga[col][trow]); + if (cr) { break; } + } + } + }, + 'for_each/below': function() { + for (trow = row + 1, max = ga[col].length; trow < max; trow++) { + if (this.is_widget(col, trow) && + $.inArray(ga[col][trow], matched) === -1 + ) { + cr = callback.call(ga[col][trow], col, trow); + matched.push(ga[col][trow]); + if (cr) { break; } + } + } + } + }; + + if (methods[action]) { + methods[action].call(this); + } + }; + + + /** + * Iterate over each widget above the column and row specified. + * + * @method for_each_widget_above + * @param {Number} col The column to start iterating. + * @param {Number} row The row to start iterating. + * @param {Function} callback The function to execute on each widget + * iteration. The value of `this` inside the function is the jQuery + * wrapped HTMLElement. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.for_each_widget_above = function(col, row, callback) { + this._traversing_widgets('for_each', 'above', col, row, callback); + return this; + }; + + + /** + * Iterate over each widget below the column and row specified. + * + * @method for_each_widget_below + * @param {Number} col The column to start iterating. + * @param {Number} row The row to start iterating. + * @param {Function} callback The function to execute on each widget + * iteration. The value of `this` inside the function is the jQuery wrapped + * HTMLElement. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.for_each_widget_below = function(col, row, callback) { + this._traversing_widgets('for_each', 'below', col, row, callback); + return this; + }; + + + /** + * Returns the highest occupied cell in the grid. + * + * @method get_highest_occupied_cell + * @return {Object} Returns an object with `col` and `row` numbers. + */ + fn.get_highest_occupied_cell = function() { + var r; + var gm = this.gridmap; + var rows = []; + var row_in_col = []; + for (var c = gm.length - 1; c >= 1; c--) { + for (r = gm[c].length - 1; r >= 1; r--) { + if (this.is_widget(c, r)) { + rows.push(r); + row_in_col[r] = c; + break; + } + } + } + + var highest_row = Math.max.apply(Math, rows); + + this.highest_occupied_cell = { + col: row_in_col[highest_row], + row: highest_row + }; + + return this.highest_occupied_cell; + }; + + + fn.get_widgets_from = function(col, row) { + var ga = this.gridmap; + var $widgets = $(); + + if (col) { + $widgets = $widgets.add( + this.$widgets.filter(function() { + var tcol = $(this).attr('data-col'); + return (tcol === col || tcol > col); + }) + ); + } + + if (row) { + $widgets = $widgets.add( + this.$widgets.filter(function() { + var trow = $(this).attr('data-row'); + return (trow === row || trow > row); + }) + ); + } + + return $widgets; + }; + + + /** + * Set the current height of the parent grid. + * + * @method set_dom_grid_height + * @return {Object} Returns the instance of the Gridster class. + */ + fn.set_dom_grid_height = function() { + var r = this.get_highest_occupied_cell().row; + this.$el.css('height', r * this.min_widget_height); + return this; + }; + + + /** + * It generates the neccessary styles to position the widgets. + * + * @method generate_stylesheet + * @param {Number} rows Number of columns. + * @param {Number} cols Number of rows. + * @return {Object} Returns the instance of the Gridster class. + */ + fn.generate_stylesheet = function(opts) { + var styles = ''; + var max_size_x = this.options.max_size_x; + var max_rows = 0; + var max_cols = 0; + var i; + var rules; + + opts || (opts = {}); + opts.cols || (opts.cols = this.cols); + opts.rows || (opts.rows = this.rows); + opts.namespace || (opts.namespace = this.options.namespace); + opts.widget_base_dimensions || + (opts.widget_base_dimensions = this.options.widget_base_dimensions); + opts.widget_margins || + (opts.widget_margins = this.options.widget_margins); + opts.min_widget_width = (opts.widget_margins[0] * 2) + + opts.widget_base_dimensions[0]; + opts.min_widget_height = (opts.widget_margins[1] * 2) + + opts.widget_base_dimensions[1]; + + // don't duplicate stylesheets for the same configuration + var serialized_opts = $.param(opts); + if ($.inArray(serialized_opts, Gridster.generated_stylesheets) >= 0) { + return false; + } + + Gridster.generated_stylesheets.push(serialized_opts); + + /* generate CSS styles for cols */ + for (i = opts.cols; i >= 0; i--) { + styles += (opts.namespace + ' [data-col="'+ (i + 1) + '"] { left:' + + ((i * opts.widget_base_dimensions[0]) + + (i * opts.widget_margins[0]) + + ((i + 1) * opts.widget_margins[0])) + 'px;} '); + } + + /* generate CSS styles for rows */ + for (i = opts.rows; i >= 0; i--) { + styles += (opts.namespace + ' [data-row="' + (i + 1) + '"] { top:' + + ((i * opts.widget_base_dimensions[1]) + + (i * opts.widget_margins[1]) + + ((i + 1) * opts.widget_margins[1]) ) + 'px;} '); + } + + for (var y = 1; y <= opts.rows; y++) { + styles += (opts.namespace + ' [data-sizey="' + y + '"] { height:' + + (y * opts.widget_base_dimensions[1] + + (y - 1) * (opts.widget_margins[1] * 2)) + 'px;}'); + } + + for (var x = 1; x <= max_size_x; x++) { + styles += (opts.namespace + ' [data-sizex="' + x + '"] { width:' + + (x * opts.widget_base_dimensions[0] + + (x - 1) * (opts.widget_margins[0] * 2)) + 'px;}'); + } + + return this.add_style_tag(styles); + }; + + + /** + * Injects the given CSS as string to the head of the document. + * + * @method add_style_tag + * @param {String} css The styles to apply. + * @return {Object} Returns the instance of the Gridster class. + */ + fn.add_style_tag = function(css) { + var d = document; + var tag = d.createElement('style'); + + d.getElementsByTagName('head')[0].appendChild(tag); + tag.setAttribute('type', 'text/css'); + + if (tag.styleSheet) { + tag.styleSheet.cssText = css; + }else{ + tag.appendChild(document.createTextNode(css)); + } + return this; + }; + + + /** + * Generates a faux grid to collide with it when a widget is dragged and + * detect row or column that we want to go. + * + * @method generate_faux_grid + * @param {Number} rows Number of columns. + * @param {Number} cols Number of rows. + * @return {Object} Returns the instance of the Gridster class. + */ + fn.generate_faux_grid = function(rows, cols) { + this.faux_grid = []; + this.gridmap = []; + var col; + var row; + for (col = cols; col > 0; col--) { + this.gridmap[col] = []; + for (row = rows; row > 0; row--) { + this.add_faux_cell(row, col); + } + } + return this; + }; + + + /** + * Add cell to the faux grid. + * + * @method add_faux_cell + * @param {Number} row The row for the new faux cell. + * @param {Number} col The col for the new faux cell. + * @return {Object} Returns the instance of the Gridster class. + */ + fn.add_faux_cell = function(row, col) { + var coords = $({ + left: this.baseX + ((col - 1) * this.min_widget_width), + top: this.baseY + (row -1) * this.min_widget_height, + width: this.min_widget_width, + height: this.min_widget_height, + col: col, + row: row, + original_col: col, + original_row: row + }).coords(); + + if (!$.isArray(this.gridmap[col])) { + this.gridmap[col] = []; + } + + this.gridmap[col][row] = false; + this.faux_grid.push(coords); + + return this; + }; + + + /** + * Add rows to the faux grid. + * + * @method add_faux_rows + * @param {Number} rows The number of rows you want to add to the faux grid. + * @return {Object} Returns the instance of the Gridster class. + */ + fn.add_faux_rows = function(rows) { + var actual_rows = this.rows; + var max_rows = actual_rows + (rows || 1); + + for (var r = max_rows; r > actual_rows; r--) { + for (var c = this.cols; c >= 1; c--) { + this.add_faux_cell(r, c); + } + } + + this.rows = max_rows; + + if (this.options.autogenerate_stylesheet) { + this.generate_stylesheet(); + } + + return this; + }; + + /** + * Add cols to the faux grid. + * + * @method add_faux_cols + * @param {Number} cols The number of cols you want to add to the faux grid. + * @return {Object} Returns the instance of the Gridster class. + */ + fn.add_faux_cols = function(cols) { + var actual_cols = this.cols; + var max_cols = actual_cols + (cols || 1); + + for (var c = actual_cols; c < max_cols; c++) { + for (var r = this.rows; r >= 1; r--) { + this.add_faux_cell(r, c); + } + } + + this.cols = max_cols; + + if (this.options.autogenerate_stylesheet) { + this.generate_stylesheet(); + } + + return this; + }; + + + /** + * Recalculates the offsets for the faux grid. You need to use it when + * the browser is resized. + * + * @method recalculate_faux_grid + * @return {Object} Returns the instance of the Gridster class. + */ + fn.recalculate_faux_grid = function() { + var aw = this.$wrapper.width(); + this.baseX = ($(window).width() - aw) / 2; + this.baseY = this.$wrapper.offset().top; + + $.each(this.faux_grid, $.proxy(function(i, coords) { + this.faux_grid[i] = coords.update({ + left: this.baseX + (coords.data.col -1) * this.min_widget_width, + top: this.baseY + (coords.data.row -1) * this.min_widget_height + }); + + }, this)); + + return this; + }; + + + /** + * Get all widgets in the DOM and register them. + * + * @method get_widgets_from_DOM + * @return {Object} Returns the instance of the Gridster class. + */ + fn.get_widgets_from_DOM = function() { + this.$widgets.each($.proxy(function(i, widget) { + this.register_widget($(widget)); + }, this)); + return this; + }; + + + /** + * Calculate columns and rows to be set based on the configuration + * parameters, grid dimensions, etc ... + * + * @method generate_grid_and_stylesheet + * @return {Object} Returns the instance of the Gridster class. + */ + fn.generate_grid_and_stylesheet = function() { + var aw = this.$wrapper.width(); + var ah = this.$wrapper.height(); + + var cols = Math.floor(aw / this.min_widget_width) + + this.options.extra_cols; + + var actual_cols = this.$widgets.map(function() { + return $(this).attr('data-col'); + }); + actual_cols = Array.prototype.slice.call(actual_cols, 0); + //needed to pass tests with phantomjs + actual_cols.length || (actual_cols = [0]); + + var min_cols = Math.max.apply(Math, actual_cols); + + // get all rows that could be occupied by the current widgets + var max_rows = this.options.extra_rows; + this.$widgets.each(function(i, w) { + max_rows += (+$(w).attr('data-sizey')); + }); + + this.cols = Math.max(min_cols, cols, this.options.min_cols); + this.rows = Math.max(max_rows, this.options.min_rows); + + this.baseX = ($(window).width() - aw) / 2; + this.baseY = this.$wrapper.offset().top; + + if (this.options.autogenerate_stylesheet) { + this.generate_stylesheet(); + } + + return this.generate_faux_grid(this.rows, this.cols); + }; + + + //jQuery adapter + $.fn.gridster = function(options) { + return this.each(function() { + if (!$(this).data('gridster')) { + $(this).data('gridster', new Gridster( this, options )); + } + }); + }; + + $.Gridster = fn; + +}(jQuery, window, document)); diff --git a/GRID/assets/jquery.js b/GRID/assets/jquery.js new file mode 100644 index 0000000..3774ff9 --- /dev/null +++ b/GRID/assets/jquery.js @@ -0,0 +1,9404 @@ +/*! + * jQuery JavaScript Library v1.7.2 + * http://jquery.com/ + * + * Copyright 2011, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Wed Mar 21 12:46:34 2012 -0700 + */ +(function( window, undefined ) { + +// Use the correct document accordingly with window argument (sandbox) +var document = window.document, + navigator = window.navigator, + location = window.location; +var jQuery = (function() { + +// Define a local copy of jQuery +var jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // A central reference to the root jQuery(document) + rootjQuery, + + // A simple way to check for HTML strings or ID strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, + + // Check if a string has a non-whitespace character in it + rnotwhite = /\S/, + + // Used for trimming whitespace + trimLeft = /^\s+/, + trimRight = /\s+$/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, + rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + + // Useragent RegExp + rwebkit = /(webkit)[ \/]([\w.]+)/, + ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, + rmsie = /(msie) ([\w.]+)/, + rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, + + // Matches dashed string for camelizing + rdashAlpha = /-([a-z]|[0-9])/ig, + rmsPrefix = /^-ms-/, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return ( letter + "" ).toUpperCase(); + }, + + // Keep a UserAgent string for use with jQuery.browser + userAgent = navigator.userAgent, + + // For matching the engine and version of the browser + browserMatch, + + // The deferred used on DOM ready + readyList, + + // The ready event handler + DOMContentLoaded, + + // Save a reference to some core methods + toString = Object.prototype.toString, + hasOwn = Object.prototype.hasOwnProperty, + push = Array.prototype.push, + slice = Array.prototype.slice, + trim = String.prototype.trim, + indexOf = Array.prototype.indexOf, + + // [[Class]] -> type pairs + class2type = {}; + +jQuery.fn = jQuery.prototype = { + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem, ret, doc; + + // Handle $(""), $(null), or $(undefined) + if ( !selector ) { + return this; + } + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // The body element only exists once, optimize finding it + if ( selector === "body" && !context && document.body ) { + this.context = document; + this[0] = document.body; + this.selector = selector; + this.length = 1; + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = quickExpr.exec( selector ); + } + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + doc = ( context ? context.ownerDocument || context : document ); + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + ret = rsingleTag.exec( selector ); + + if ( ret ) { + if ( jQuery.isPlainObject( context ) ) { + selector = [ document.createElement( ret[1] ) ]; + jQuery.fn.attr.call( selector, context, true ); + + } else { + selector = [ doc.createElement( ret[1] ) ]; + } + + } else { + ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); + selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes; + } + + return jQuery.merge( this, selector ); + + // HANDLE: $("#id") + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.7.2", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return slice.call( this, 0 ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new jQuery matched element set + var ret = this.constructor(); + + if ( jQuery.isArray( elems ) ) { + push.apply( ret, elems ); + + } else { + jQuery.merge( ret, elems ); + } + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) { + ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Attach the listeners + jQuery.bindReady(); + + // Add the callback + readyList.add( fn ); + + return this; + }, + + eq: function( i ) { + i = +i; + return i === -1 ? + this.slice( i ) : + this.slice( i, i + 1 ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + // Either a released hold or an DOMready/load event and not yet ready + if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready, 1 ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.fireWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger( "ready" ).off( "ready" ); + } + } + }, + + bindReady: function() { + if ( readyList ) { + return; + } + + readyList = jQuery.Callbacks( "once memory" ); + + // Catch cases where $(document).ready() is called after the + // browser event has already occurred. + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + return setTimeout( jQuery.ready, 1 ); + } + + // Mozilla, Opera and webkit nightlies currently support this event + if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else if ( document.attachEvent ) { + // ensure firing before onload, + // maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", DOMContentLoaded ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var toplevel = false; + + try { + toplevel = window.frameElement == null; + } catch(e) {} + + if ( document.documentElement.doScroll && toplevel ) { + doScrollCheck(); + } + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + isWindow: function( obj ) { + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + return !isNaN( parseFloat(obj) ) && isFinite( obj ); + }, + + type: function( obj ) { + return obj == null ? + String( obj ) : + class2type[ toString.call(obj) ] || "object"; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + for ( var name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw new Error( msg ); + }, + + parseJSON: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return ( new Function( "return " + data ) )(); + + } + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + parseXML: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + var xml, tmp; + try { + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && rnotwhite.test( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + }, + + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, + length = object.length, + isObj = length === undefined || jQuery.isFunction( object ); + + if ( args ) { + if ( isObj ) { + for ( name in object ) { + if ( callback.apply( object[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( object[ i++ ], args ) === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in object ) { + if ( callback.call( object[ name ], name, object[ name ] ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) { + break; + } + } + } + } + + return object; + }, + + // Use native String.trim function wherever possible + trim: trim ? + function( text ) { + return text == null ? + "" : + trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + text.toString().replace( trimLeft, "" ).replace( trimRight, "" ); + }, + + // results is for internal usage only + makeArray: function( array, results ) { + var ret = results || []; + + if ( array != null ) { + // The window, strings (and functions) also have 'length' + // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 + var type = jQuery.type( array ); + + if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) { + push.call( ret, array ); + } else { + jQuery.merge( ret, array ); + } + } + + return ret; + }, + + inArray: function( elem, array, i ) { + var len; + + if ( array ) { + if ( indexOf ) { + return indexOf.call( array, elem, i ); + } + + len = array.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in array && array[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var i = first.length, + j = 0; + + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var ret = [], retVal; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( var i = 0, length = elems.length; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, key, ret = [], + i = 0, + length = elems.length, + // jquery objects are treated as arrays + isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( key in elems ) { + value = callback( elems[ key ], key, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return ret.concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + if ( typeof context === "string" ) { + var tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + var args = slice.call( arguments, 2 ), + proxy = function() { + return fn.apply( context, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; + + return proxy; + }, + + // Mutifunctional method to get and set values to a collection + // The value/s can optionally be executed if it's a function + access: function( elems, fn, key, value, chainable, emptyGet, pass ) { + var exec, + bulk = key == null, + i = 0, + length = elems.length; + + // Sets many values + if ( key && typeof key === "object" ) { + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], 1, emptyGet, value ); + } + chainable = 1; + + // Sets one value + } else if ( value !== undefined ) { + // Optionally, function values get executed if exec is true + exec = pass === undefined && jQuery.isFunction( value ); + + if ( bulk ) { + // Bulk operations only iterate when executing function values + if ( exec ) { + exec = fn; + fn = function( elem, key, value ) { + return exec.call( jQuery( elem ), value ); + }; + + // Otherwise they run against the entire set + } else { + fn.call( elems, value ); + fn = null; + } + } + + if ( fn ) { + for (; i < length; i++ ) { + fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + } + } + + chainable = 1; + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[0], key ) : emptyGet; + }, + + now: function() { + return ( new Date() ).getTime(); + }, + + // Use of jQuery.browser is frowned upon. + // More details: http://docs.jquery.com/Utilities/jQuery.browser + uaMatch: function( ua ) { + ua = ua.toLowerCase(); + + var match = rwebkit.exec( ua ) || + ropera.exec( ua ) || + rmsie.exec( ua ) || + ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || + []; + + return { browser: match[1] || "", version: match[2] || "0" }; + }, + + sub: function() { + function jQuerySub( selector, context ) { + return new jQuerySub.fn.init( selector, context ); + } + jQuery.extend( true, jQuerySub, this ); + jQuerySub.superclass = this; + jQuerySub.fn = jQuerySub.prototype = this(); + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.sub = this.sub; + jQuerySub.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { + context = jQuerySub( context ); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); + }; + jQuerySub.fn.init.prototype = jQuerySub.fn; + var rootjQuerySub = jQuerySub(document); + return jQuerySub; + }, + + browser: {} +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +browserMatch = jQuery.uaMatch( userAgent ); +if ( browserMatch.browser ) { + jQuery.browser[ browserMatch.browser ] = true; + jQuery.browser.version = browserMatch.version; +} + +// Deprecated, use jQuery.browser.webkit instead +if ( jQuery.browser.webkit ) { + jQuery.browser.safari = true; +} + +// IE doesn't match non-breaking spaces with \s +if ( rnotwhite.test( "\xA0" ) ) { + trimLeft = /^[\s\xA0]+/; + trimRight = /[\s\xA0]+$/; +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); + +// Cleanup functions for the document ready method +if ( document.addEventListener ) { + DOMContentLoaded = function() { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + }; + +} else if ( document.attachEvent ) { + DOMContentLoaded = function() { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( document.readyState === "complete" ) { + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }; +} + +// The DOM ready check for Internet Explorer +function doScrollCheck() { + if ( jQuery.isReady ) { + return; + } + + try { + // If IE is used, use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + document.documentElement.doScroll("left"); + } catch(e) { + setTimeout( doScrollCheck, 1 ); + return; + } + + // and execute any waiting functions + jQuery.ready(); +} + +return jQuery; + +})(); + + +// String to Object flags format cache +var flagsCache = {}; + +// Convert String-formatted flags into Object-formatted ones and store in cache +function createFlags( flags ) { + var object = flagsCache[ flags ] = {}, + i, length; + flags = flags.split( /\s+/ ); + for ( i = 0, length = flags.length; i < length; i++ ) { + object[ flags[i] ] = true; + } + return object; +} + +/* + * Create a callback list using the following parameters: + * + * flags: an optional list of space-separated flags that will change how + * the callback list behaves + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible flags: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( flags ) { + + // Convert flags from String-formatted to Object-formatted + // (we check in cache first) + flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {}; + + var // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = [], + // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // Flag to know if list is currently firing + firing, + // First callback to fire (used internally by add and fireWith) + firingStart, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // Add one or several callbacks to the list + add = function( args ) { + var i, + length, + elem, + type, + actual; + for ( i = 0, length = args.length; i < length; i++ ) { + elem = args[ i ]; + type = jQuery.type( elem ); + if ( type === "array" ) { + // Inspect recursively + add( elem ); + } else if ( type === "function" ) { + // Add if not in unique mode and callback is not in + if ( !flags.unique || !self.has( elem ) ) { + list.push( elem ); + } + } + } + }, + // Fire callbacks + fire = function( context, args ) { + args = args || []; + memory = !flags.memory || [ context, args ]; + fired = true; + firing = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) { + memory = true; // Mark as halted + break; + } + } + firing = false; + if ( list ) { + if ( !flags.once ) { + if ( stack && stack.length ) { + memory = stack.shift(); + self.fireWith( memory[ 0 ], memory[ 1 ] ); + } + } else if ( memory === true ) { + self.disable(); + } else { + list = []; + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + var length = list.length; + add( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away, unless previous + // firing was halted (stopOnFalse) + } else if ( memory && memory !== true ) { + firingStart = length; + fire( memory[ 0 ], memory[ 1 ] ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + var args = arguments, + argIndex = 0, + argLength = args.length; + for ( ; argIndex < argLength ; argIndex++ ) { + for ( var i = 0; i < list.length; i++ ) { + if ( args[ argIndex ] === list[ i ] ) { + // Handle firingIndex and firingLength + if ( firing ) { + if ( i <= firingLength ) { + firingLength--; + if ( i <= firingIndex ) { + firingIndex--; + } + } + } + // Remove the element + list.splice( i--, 1 ); + // If we have some unicity property then + // we only need to do this once + if ( flags.unique ) { + break; + } + } + } + } + } + return this; + }, + // Control if a given callback is in the list + has: function( fn ) { + if ( list ) { + var i = 0, + length = list.length; + for ( ; i < length; i++ ) { + if ( fn === list[ i ] ) { + return true; + } + } + } + return false; + }, + // Remove all callbacks from the list + empty: function() { + list = []; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory || memory === true ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( stack ) { + if ( firing ) { + if ( !flags.once ) { + stack.push( [ context, args ] ); + } + } else if ( !( flags.once && memory ) ) { + fire( context, args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + + + +var // Static reference to slice + sliceDeferred = [].slice; + +jQuery.extend({ + + Deferred: function( func ) { + var doneList = jQuery.Callbacks( "once memory" ), + failList = jQuery.Callbacks( "once memory" ), + progressList = jQuery.Callbacks( "memory" ), + state = "pending", + lists = { + resolve: doneList, + reject: failList, + notify: progressList + }, + promise = { + done: doneList.add, + fail: failList.add, + progress: progressList.add, + + state: function() { + return state; + }, + + // Deprecated + isResolved: doneList.fired, + isRejected: failList.fired, + + then: function( doneCallbacks, failCallbacks, progressCallbacks ) { + deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks ); + return this; + }, + always: function() { + deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments ); + return this; + }, + pipe: function( fnDone, fnFail, fnProgress ) { + return jQuery.Deferred(function( newDefer ) { + jQuery.each( { + done: [ fnDone, "resolve" ], + fail: [ fnFail, "reject" ], + progress: [ fnProgress, "notify" ] + }, function( handler, data ) { + var fn = data[ 0 ], + action = data[ 1 ], + returned; + if ( jQuery.isFunction( fn ) ) { + deferred[ handler ](function() { + returned = fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); + } + }); + } else { + deferred[ handler ]( newDefer[ action ] ); + } + }); + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + if ( obj == null ) { + obj = promise; + } else { + for ( var key in promise ) { + obj[ key ] = promise[ key ]; + } + } + return obj; + } + }, + deferred = promise.promise({}), + key; + + for ( key in lists ) { + deferred[ key ] = lists[ key ].fire; + deferred[ key + "With" ] = lists[ key ].fireWith; + } + + // Handle state + deferred.done( function() { + state = "resolved"; + }, failList.disable, progressList.lock ).fail( function() { + state = "rejected"; + }, doneList.disable, progressList.lock ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( firstParam ) { + var args = sliceDeferred.call( arguments, 0 ), + i = 0, + length = args.length, + pValues = new Array( length ), + count = length, + pCount = length, + deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ? + firstParam : + jQuery.Deferred(), + promise = deferred.promise(); + function resolveFunc( i ) { + return function( value ) { + args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + if ( !( --count ) ) { + deferred.resolveWith( deferred, args ); + } + }; + } + function progressFunc( i ) { + return function( value ) { + pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + deferred.notifyWith( promise, pValues ); + }; + } + if ( length > 1 ) { + for ( ; i < length; i++ ) { + if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) { + args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) ); + } else { + --count; + } + } + if ( !count ) { + deferred.resolveWith( deferred, args ); + } + } else if ( deferred !== firstParam ) { + deferred.resolveWith( deferred, length ? [ firstParam ] : [] ); + } + return promise; + } +}); + + + + +jQuery.support = (function() { + + var support, + all, + a, + select, + opt, + input, + fragment, + tds, + events, + eventName, + i, + isSupported, + div = document.createElement( "div" ), + documentElement = document.documentElement; + + // Preliminary tests + div.setAttribute("className", "t"); + div.innerHTML = "
    a"; + + all = div.getElementsByTagName( "*" ); + a = div.getElementsByTagName( "a" )[ 0 ]; + + // Can't get basic test support + if ( !all || !all.length || !a ) { + return {}; + } + + // First batch of supports tests + select = document.createElement( "select" ); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName( "input" )[ 0 ]; + + support = { + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: ( div.firstChild.nodeType === 3 ), + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName("tbody").length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName("link").length, + + // Get the style information from getAttribute + // (IE uses .cssText instead) + style: /top/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: ( a.getAttribute("href") === "/a" ), + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.55/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Make sure that if no value is specified for a checkbox + // that it defaults to "on". + // (WebKit defaults to "" instead) + checkOn: ( input.value === "on" ), + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: opt.selected, + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + getSetAttribute: div.className !== "t", + + // Tests for enctype support on a form(#6743) + enctype: !!document.createElement("form").enctype, + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>", + + // Will be defined later + submitBubbles: true, + changeBubbles: true, + focusinBubbles: false, + deleteExpando: true, + noCloneEvent: true, + inlineBlockNeedsLayout: false, + shrinkWrapBlocks: false, + reliableMarginRight: true, + pixelMargin: true + }; + + // jQuery.boxModel DEPRECATED in 1.3, use jQuery.support.boxModel instead + jQuery.boxModel = support.boxModel = (document.compatMode === "CSS1Compat"); + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Test to see if it's possible to delete an expando from an element + // Fails in Internet Explorer + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { + div.attachEvent( "onclick", function() { + // Cloning a node shouldn't copy over any + // bound event handlers (IE does this) + support.noCloneEvent = false; + }); + div.cloneNode( true ).fireEvent( "onclick" ); + } + + // Check if a radio maintains its value + // after being appended to the DOM + input = document.createElement("input"); + input.value = "t"; + input.setAttribute("type", "radio"); + support.radioValue = input.value === "t"; + + input.setAttribute("checked", "checked"); + + // #11217 - WebKit loses check when the name is after the checked attribute + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + fragment = document.createDocumentFragment(); + fragment.appendChild( div.lastChild ); + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + fragment.removeChild( input ); + fragment.appendChild( div ); + + // Technique from Juriy Zaytsev + // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ + // We only care about the case where non-standard event systems + // are used, namely in IE. Short-circuiting here helps us to + // avoid an eval call (in setAttribute) which can cause CSP + // to go haywire. See: https://developer.mozilla.org/en/Security/CSP + if ( div.attachEvent ) { + for ( i in { + submit: 1, + change: 1, + focusin: 1 + }) { + eventName = "on" + i; + isSupported = ( eventName in div ); + if ( !isSupported ) { + div.setAttribute( eventName, "return;" ); + isSupported = ( typeof div[ eventName ] === "function" ); + } + support[ i + "Bubbles" ] = isSupported; + } + } + + fragment.removeChild( div ); + + // Null elements to avoid leaks in IE + fragment = select = opt = div = input = null; + + // Run tests that need a body at doc ready + jQuery(function() { + var container, outer, inner, table, td, offsetSupport, + marginDiv, conMarginTop, style, html, positionTopLeftWidthHeight, + paddingMarginBorderVisibility, paddingMarginBorder, + body = document.getElementsByTagName("body")[0]; + + if ( !body ) { + // Return for frameset docs that don't have a body + return; + } + + conMarginTop = 1; + paddingMarginBorder = "padding:0;margin:0;border:"; + positionTopLeftWidthHeight = "position:absolute;top:0;left:0;width:1px;height:1px;"; + paddingMarginBorderVisibility = paddingMarginBorder + "0;visibility:hidden;"; + style = "style='" + positionTopLeftWidthHeight + paddingMarginBorder + "5px solid #000;"; + html = "
    " + + "" + + "
    "; + + container = document.createElement("div"); + container.style.cssText = paddingMarginBorderVisibility + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px"; + body.insertBefore( container, body.firstChild ); + + // Construct the test element + div = document.createElement("div"); + container.appendChild( div ); + + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + // (only IE 8 fails this test) + div.innerHTML = "
    t
    "; + tds = div.getElementsByTagName( "td" ); + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Check if empty table cells still have offsetWidth/Height + // (IE <= 8 fail this test) + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. For more + // info see bug #3333 + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + if ( window.getComputedStyle ) { + div.innerHTML = ""; + marginDiv = document.createElement( "div" ); + marginDiv.style.width = "0"; + marginDiv.style.marginRight = "0"; + div.style.width = "2px"; + div.appendChild( marginDiv ); + support.reliableMarginRight = + ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; + } + + if ( typeof div.style.zoom !== "undefined" ) { + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + // (IE < 8 does this) + div.innerHTML = ""; + div.style.width = div.style.padding = "1px"; + div.style.border = 0; + div.style.overflow = "hidden"; + div.style.display = "inline"; + div.style.zoom = 1; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); + + // Check if elements with layout shrink-wrap their children + // (IE 6 does this) + div.style.display = "block"; + div.style.overflow = "visible"; + div.innerHTML = "
    "; + support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); + } + + div.style.cssText = positionTopLeftWidthHeight + paddingMarginBorderVisibility; + div.innerHTML = html; + + outer = div.firstChild; + inner = outer.firstChild; + td = outer.nextSibling.firstChild.firstChild; + + offsetSupport = { + doesNotAddBorder: ( inner.offsetTop !== 5 ), + doesAddBorderForTableAndCells: ( td.offsetTop === 5 ) + }; + + inner.style.position = "fixed"; + inner.style.top = "20px"; + + // safari subtracts parent border width here which is 5px + offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 ); + inner.style.position = inner.style.top = ""; + + outer.style.overflow = "hidden"; + outer.style.position = "relative"; + + offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 ); + offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop ); + + if ( window.getComputedStyle ) { + div.style.marginTop = "1%"; + support.pixelMargin = ( window.getComputedStyle( div, null ) || { marginTop: 0 } ).marginTop !== "1%"; + } + + if ( typeof container.style.zoom !== "undefined" ) { + container.style.zoom = 1; + } + + body.removeChild( container ); + marginDiv = div = container = null; + + jQuery.extend( support, offsetSupport ); + }); + + return support; +})(); + + + + +var rbrace = /^(?:\{.*\}|\[.*\])$/, + rmultiDash = /([A-Z])/g; + +jQuery.extend({ + cache: {}, + + // Please use with caution + uuid: 0, + + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var privateCache, thisCache, ret, + internalKey = jQuery.expando, + getByName = typeof name === "string", + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey, + isEvents = name === "events"; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + elem[ internalKey ] = id = ++jQuery.uuid; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + cache[ id ] = {}; + + // Avoids exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + privateCache = thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Users should not attempt to inspect the internal events object using jQuery.data, + // it is undocumented and subject to change. But does anyone listen? No. + if ( isEvents && !thisCache[ name ] ) { + return privateCache.events; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( getByName ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; + }, + + removeData: function( elem, name, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, i, l, + + // Reference to internal data cache key + internalKey = jQuery.expando, + + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + + // See jQuery.data for more information + id = isNode ? elem[ internalKey ] : internalKey; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split( " " ); + } + } + } + + for ( i = 0, l = name.length; i < l; i++ ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject(cache[ id ]) ) { + return; + } + } + + // Browsers that fail expando deletion also refuse to delete expandos on + // the window, but it will allow it on all other JS objects; other browsers + // don't care + // Ensure that `cache` is not a window object #10080 + if ( jQuery.support.deleteExpando || !cache.setInterval ) { + delete cache[ id ]; + } else { + cache[ id ] = null; + } + + // We destroyed the cache and need to eliminate the expando on the node to avoid + // false lookups in the cache for entries that no longer exist + if ( isNode ) { + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( jQuery.support.deleteExpando ) { + delete elem[ internalKey ]; + } else if ( elem.removeAttribute ) { + elem.removeAttribute( internalKey ); + } else { + elem[ internalKey ] = null; + } + } + }, + + // For internal use only. + _data: function( elem, name, data ) { + return jQuery.data( elem, name, data, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + if ( elem.nodeName ) { + var match = jQuery.noData[ elem.nodeName.toLowerCase() ]; + + if ( match ) { + return !(match === true || elem.getAttribute("classid") !== match); + } + } + + return true; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var parts, part, attr, name, l, + elem = this[0], + i = 0, + data = null; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + attr = elem.attributes; + for ( l = attr.length; i < l; i++ ) { + name = attr[i].name; + + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.substring(5) ); + + dataAttr( elem, name, data[ name ] ); + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + parts = key.split( ".", 2 ); + parts[1] = parts[1] ? "." + parts[1] : ""; + part = parts[1] + "!"; + + return jQuery.access( this, function( value ) { + + if ( value === undefined ) { + data = this.triggerHandler( "getData" + part, [ parts[0] ] ); + + // Try to fetch any internally stored data first + if ( data === undefined && elem ) { + data = jQuery.data( elem, key ); + data = dataAttr( elem, key, data ); + } + + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + } + + parts[1] = value; + this.each(function() { + var self = jQuery( this ); + + self.triggerHandler( "setData" + part, parts ); + jQuery.data( this, key, value ); + self.triggerHandler( "changeData" + part, parts ); + }); + }, null, value, arguments.length > 1, null, false ); + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + jQuery.isNumeric( data ) ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + for ( var name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} + + + + +function handleQueueMarkDefer( elem, type, src ) { + var deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + defer = jQuery._data( elem, deferDataKey ); + if ( defer && + ( src === "queue" || !jQuery._data(elem, queueDataKey) ) && + ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) { + // Give room for hard-coded callbacks to fire first + // and eventually mark/queue something else on the element + setTimeout( function() { + if ( !jQuery._data( elem, queueDataKey ) && + !jQuery._data( elem, markDataKey ) ) { + jQuery.removeData( elem, deferDataKey, true ); + defer.fire(); + } + }, 0 ); + } +} + +jQuery.extend({ + + _mark: function( elem, type ) { + if ( elem ) { + type = ( type || "fx" ) + "mark"; + jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 ); + } + }, + + _unmark: function( force, elem, type ) { + if ( force !== true ) { + type = elem; + elem = force; + force = false; + } + if ( elem ) { + type = type || "fx"; + var key = type + "mark", + count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 ); + if ( count ) { + jQuery._data( elem, key, count ); + } else { + jQuery.removeData( elem, key, true ); + handleQueueMarkDefer( elem, type, "mark" ); + } + } + }, + + queue: function( elem, type, data ) { + var q; + if ( elem ) { + type = ( type || "fx" ) + "queue"; + q = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !q || jQuery.isArray(data) ) { + q = jQuery._data( elem, type, jQuery.makeArray(data) ); + } else { + q.push( data ); + } + } + return q || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + fn = queue.shift(), + hooks = {}; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + } + + if ( fn ) { + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + jQuery._data( elem, type + ".run", hooks ); + fn.call( elem, function() { + jQuery.dequeue( elem, type ); + }, hooks ); + } + + if ( !queue.length ) { + jQuery.removeData( elem, type + "queue " + type + ".run", true ); + handleQueueMarkDefer( elem, type, "queue" ); + } + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, object ) { + if ( typeof type !== "string" ) { + object = type; + type = undefined; + } + type = type || "fx"; + var defer = jQuery.Deferred(), + elements = this, + i = elements.length, + count = 1, + deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + tmp; + function resolve() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + } + while( i-- ) { + if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) || + ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) || + jQuery.data( elements[ i ], markDataKey, undefined, true ) ) && + jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) { + count++; + tmp.add( resolve ); + } + } + resolve(); + return defer.promise( object ); + } +}); + + + + +var rclass = /[\n\t\r]/g, + rspace = /\s+/, + rreturn = /\r/g, + rtype = /^(?:button|input)$/i, + rfocusable = /^(?:button|input|object|select|textarea)$/i, + rclickable = /^a(?:rea)?$/i, + rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, + getSetAttribute = jQuery.support.getSetAttribute, + nodeHook, boolHook, fixSpecified; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classNames, i, l, elem, + setClass, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call(this, j, this.className) ); + }); + } + + if ( value && typeof value === "string" ) { + classNames = value.split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 ) { + if ( !elem.className && classNames.length === 1 ) { + elem.className = value; + + } else { + setClass = " " + elem.className + " "; + + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { + setClass += classNames[ c ] + " "; + } + } + elem.className = jQuery.trim( setClass ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classNames, i, l, elem, className, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call(this, j, this.className) ); + }); + } + + if ( (value && typeof value === "string") || value === undefined ) { + classNames = ( value || "" ).split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 && elem.className ) { + if ( value ) { + className = (" " + elem.className + " ").replace( rclass, " " ); + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + className = className.replace(" " + classNames[ c ] + " ", " "); + } + elem.className = jQuery.trim( className ); + + } else { + elem.className = ""; + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.split( rspace ); + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space seperated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + } else if ( type === "undefined" || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // toggle whole className + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var hooks, ret, isFunction, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var self = jQuery(this), val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, self.val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + }, + select: { + get: function( elem ) { + var value, i, max, option, + index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type === "select-one"; + + // Nothing was selected + if ( index < 0 ) { + return null; + } + + // Loop through all the selected options + i = one ? index : 0; + max = one ? index + 1 : options.length; + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Don't return options that are disabled or in a disabled optgroup + if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && + (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + // Fixes Bug #2551 -- select.val() broken in IE after form.reset() + if ( one && !values.length && options.length ) { + return jQuery( options[ index ] ).val(); + } + + return values; + }, + + set: function( elem, value ) { + var values = jQuery.makeArray( value ); + + jQuery(elem).find("option").each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + attrFn: { + val: true, + css: true, + html: true, + text: true, + data: true, + width: true, + height: true, + offset: true + }, + + attr: function( elem, name, value, pass ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( pass && name in jQuery.attrFn ) { + return jQuery( elem )[ name ]( value ); + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( notxml ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + + } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, "" + value ); + return value; + } + + } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + + ret = elem.getAttribute( name ); + + // Non-existent attributes return null, we normalize to undefined + return ret === null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var propName, attrNames, name, l, isBool, + i = 0; + + if ( value && elem.nodeType === 1 ) { + attrNames = value.toLowerCase().split( rspace ); + l = attrNames.length; + + for ( ; i < l; i++ ) { + name = attrNames[ i ]; + + if ( name ) { + propName = jQuery.propFix[ name ] || name; + isBool = rboolean.test( name ); + + // See #9699 for explanation of this approach (setting first, then removal) + // Do not do this for boolean attributes (see #10870) + if ( !isBool ) { + jQuery.attr( elem, name, "" ); + } + elem.removeAttribute( getSetAttribute ? name : propName ); + + // Set corresponding property to false for boolean attributes + if ( isBool && propName in elem ) { + elem[ propName ] = false; + } + } + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + // We can't allow the type property to be changed (since it causes problems in IE) + if ( rtype.test( elem.nodeName ) && elem.parentNode ) { + jQuery.error( "type property can't be changed" ); + } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to it's default in case type is set after value + // This is for element creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + }, + // Use the value property for back compat + // Use the nodeHook for button elements in IE6/7 (#1954) + value: { + get: function( elem, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.get( elem, name ); + } + return name in elem ? + elem.value : + null; + }, + set: function( elem, value, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.set( elem, value, name ); + } + // Does not return so that setAttribute is also used + elem.value = value; + } + } + }, + + propFix: { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + cellpadding: "cellPadding", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder", + contenteditable: "contentEditable" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + return ( elem[ name ] = value ); + } + + } else { + if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + var attributeNode = elem.getAttributeNode("tabindex"); + + return attributeNode && attributeNode.specified ? + parseInt( attributeNode.value, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + } + } +}); + +// Add the tabIndex propHook to attrHooks for back-compat (different case is intentional) +jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex; + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + // Fall back to attribute presence where some booleans are not supported + var attrNode, + property = jQuery.prop( elem, name ); + return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + var propName; + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + // value is true since we know at this point it's type boolean and not false + // Set boolean attributes to the same name and set the DOM property + propName = jQuery.propFix[ name ] || name; + if ( propName in elem ) { + // Only set the IDL specifically if it already exists on the element + elem[ propName ] = true; + } + + elem.setAttribute( name, name.toLowerCase() ); + } + return name; + } +}; + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !getSetAttribute ) { + + fixSpecified = { + name: true, + id: true, + coords: true + }; + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = jQuery.valHooks.button = { + get: function( elem, name ) { + var ret; + ret = elem.getAttributeNode( name ); + return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ? + ret.nodeValue : + undefined; + }, + set: function( elem, value, name ) { + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + ret = document.createAttribute( name ); + elem.setAttributeNode( ret ); + } + return ( ret.nodeValue = value + "" ); + } + }; + + // Apply the nodeHook to tabindex + jQuery.attrHooks.tabindex.set = nodeHook.set; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }); + }); + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + get: nodeHook.get, + set: function( elem, value, name ) { + if ( value === "" ) { + value = "false"; + } + nodeHook.set( elem, value, name ); + } + }; +} + + +// Some attributes require a special call on IE +if ( !jQuery.support.hrefNormalized ) { + jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + get: function( elem ) { + var ret = elem.getAttribute( name, 2 ); + return ret === null ? undefined : ret; + } + }); + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Normalize to lowercase since IE uppercases css property names + return elem.style.cssText.toLowerCase() || undefined; + }, + set: function( elem, value ) { + return ( elem.style.cssText = "" + value ); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + } + }); +} + +// IE6/7 call enctype encoding +if ( !jQuery.support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + +// Radios and checkboxes getter/setter +if ( !jQuery.support.checkOn ) { + jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + get: function( elem ) { + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + } + }; + }); +} +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }); +}); + + + + +var rformElems = /^(?:textarea|input|select)$/i, + rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/, + rhoverHack = /(?:^|\s)hover(\.\S+)?\b/, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/, + quickParse = function( selector ) { + var quick = rquickIs.exec( selector ); + if ( quick ) { + // 0 1 2 3 + // [ _, tag, id, class ] + quick[1] = ( quick[1] || "" ).toLowerCase(); + quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" ); + } + return quick; + }, + quickIs = function( elem, m ) { + var attrs = elem.attributes || {}; + return ( + (!m[1] || elem.nodeName.toLowerCase() === m[1]) && + (!m[2] || (attrs.id || {}).value === m[2]) && + (!m[3] || m[3].test( (attrs[ "class" ] || {}).value )) + ); + }, + hoverHack = function( events ) { + return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" ); + }; + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + add: function( elem, types, handler, data, selector ) { + + var elemData, eventHandle, events, + t, tns, type, namespaces, handleObj, + handleObjIn, quick, handlers, special; + + // Don't attach events to noData or text/comment nodes (allow plain objects tho) + if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + events = elemData.events; + if ( !events ) { + elemData.events = events = {}; + } + eventHandle = elemData.handle; + if ( !eventHandle ) { + elemData.handle = eventHandle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = jQuery.trim( hoverHack(types) ).split( " " ); + for ( t = 0; t < types.length; t++ ) { + + tns = rtypenamespace.exec( types[t] ) || []; + type = tns[1]; + namespaces = ( tns[2] || "" ).split( "." ).sort(); + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: tns[1], + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + quick: selector && quickParse( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + handlers = events[ type ]; + if ( !handlers ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + global: {}, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var elemData = jQuery.hasData( elem ) && jQuery._data( elem ), + t, tns, type, origType, namespaces, origCount, + j, events, special, handle, eventType, handleObj; + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = jQuery.trim( hoverHack( types || "" ) ).split(" "); + for ( t = 0; t < types.length; t++ ) { + tns = rtypenamespace.exec( types[t] ) || []; + type = origType = tns[1]; + namespaces = tns[2]; + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector? special.delegateType : special.bindType ) || type; + eventType = events[ type ] || []; + origCount = eventType.length; + namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null; + + // Remove matching events + for ( j = 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !namespaces || namespaces.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + eventType.splice( j--, 1 ); + + if ( handleObj.selector ) { + eventType.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( eventType.length === 0 && origCount !== eventType.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + handle = elemData.handle; + if ( handle ) { + handle.elem = null; + } + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery.removeData( elem, [ "events", "handle" ], true ); + } + }, + + // Events that are safe to short-circuit if no handlers are attached. + // Native DOM events should not be added, they may have inline handlers. + customEvent: { + "getData": true, + "setData": true, + "changeData": true + }, + + trigger: function( event, data, elem, onlyHandlers ) { + // Don't do events on text and comment nodes + if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) { + return; + } + + // Event object or event type + var type = event.type || event, + namespaces = [], + cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType; + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "!" ) >= 0 ) { + // Exclusive events trigger only for the exact event (no namespaces) + type = type.slice(0, -1); + exclusive = true; + } + + if ( type.indexOf( "." ) >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + + if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { + // No jQuery handlers for this event type, and it can't have inline handlers + return; + } + + // Caller can pass in an Event, Object, or just an event type string + event = typeof event === "object" ? + // jQuery.Event object + event[ jQuery.expando ] ? event : + // Object literal + new jQuery.Event( type, event ) : + // Just the event type (string) + new jQuery.Event( type ); + + event.type = type; + event.isTrigger = true; + event.exclusive = exclusive; + event.namespace = namespaces.join( "." ); + event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null; + ontype = type.indexOf( ":" ) < 0 ? "on" + type : ""; + + // Handle a global trigger + if ( !elem ) { + + // TODO: Stop taunting the data cache; remove global events and always attach to document + cache = jQuery.cache; + for ( i in cache ) { + if ( cache[ i ].events && cache[ i ].events[ type ] ) { + jQuery.event.trigger( event, data, cache[ i ].handle.elem, true ); + } + } + return; + } + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data != null ? jQuery.makeArray( data ) : []; + data.unshift( event ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + eventPath = [[ elem, special.bindType || type ]]; + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode; + old = null; + for ( ; cur; cur = cur.parentNode ) { + eventPath.push([ cur, bubbleType ]); + old = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( old && old === elem.ownerDocument ) { + eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]); + } + } + + // Fire handlers on the event path + for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) { + + cur = eventPath[i][0]; + event.type = eventPath[i][1]; + + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + // Note that this is a bare JS function and not a jQuery handler + handle = ontype && cur[ ontype ]; + if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) { + event.preventDefault(); + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && + !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + // IE<9 dies on focus/blur to hidden element (#1486) + if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + old = elem[ ontype ]; + + if ( old ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + elem[ type ](); + jQuery.event.triggered = undefined; + + if ( old ) { + elem[ ontype ] = old; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event || window.event ); + + var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []), + delegateCount = handlers.delegateCount, + args = [].slice.call( arguments, 0 ), + run_all = !event.exclusive && !event.namespace, + special = jQuery.event.special[ event.type ] || {}, + handlerQueue = [], + i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers that should run if there are delegated events + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && !(event.button && event.type === "click") ) { + + // Pregenerate a single jQuery object for reuse with .is() + jqcur = jQuery(this); + jqcur.context = this.ownerDocument || this; + + for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { + + // Don't process events on disabled elements (#6911, #8165) + if ( cur.disabled !== true ) { + selMatch = {}; + matches = []; + jqcur[0] = cur; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + sel = handleObj.selector; + + if ( selMatch[ sel ] === undefined ) { + selMatch[ sel ] = ( + handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel ) + ); + } + if ( selMatch[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, matches: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( handlers.length > delegateCount ) { + handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) }); + } + + // Run delegates first; they may want to stop propagation beneath us + for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) { + matched = handlerQueue[ i ]; + event.currentTarget = matched.elem; + + for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) { + handleObj = matched.matches[ j ]; + + // Triggered event must either 1) be non-exclusive and have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) { + + event.data = handleObj.data; + event.handleObj = handleObj; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + event.result = ret; + if ( ret === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 *** + props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, + originalEvent = event, + fixHook = jQuery.event.fixHooks[ event.type ] || {}, + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = jQuery.Event( originalEvent ); + + for ( i = copy.length; i; ) { + prop = copy[ --i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Target should not be a text node (#504, Safari) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8) + if ( event.metaKey === undefined ) { + event.metaKey = event.ctrlKey; + } + + return fixHook.filter? fixHook.filter( event, originalEvent ) : event; + }, + + special: { + ready: { + // Make sure the ready event is setup + setup: jQuery.bindReady + }, + + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + + focus: { + delegateType: "focusin" + }, + blur: { + delegateType: "focusout" + }, + + beforeunload: { + setup: function( data, namespaces, eventHandle ) { + // We only want to do this special case on windows + if ( jQuery.isWindow( this ) ) { + this.onbeforeunload = eventHandle; + } + }, + + teardown: function( namespaces, eventHandle ) { + if ( this.onbeforeunload === eventHandle ) { + this.onbeforeunload = null; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +// Some plugins are using, but it's undocumented/deprecated and will be removed. +// The 1.7 special event interface should provide all the hooks needed now. +jQuery.event.handle = jQuery.event.dispatch; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + if ( elem.detachEvent ) { + elem.detachEvent( "on" + type, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +function returnFalse() { + return false; +} +function returnTrue() { + return true; +} + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + preventDefault: function() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + + // if preventDefault exists run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // otherwise set the returnValue property of the original event to false (IE) + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + // if stopPropagation exists run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + // otherwise set the cancelBubble property of the original event to true (IE) + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var target = this, + related = event.relatedTarget, + handleObj = event.handleObj, + selector = handleObj.selector, + ret; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// IE submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !form._submit_attached ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submit_bubble = true; + }); + form._submit_attached = true; + } + }); + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( event._submit_bubble ) { + delete event._submit_bubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + } + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !jQuery.support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + jQuery.event.simulate( "change", this, event, true ); + } + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + elem._change_attached = true; + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0, + handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { // && selector != null + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + var handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( var type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + bind: function( types, data, fn ) { + return this.on( types, null, data, fn ); + }, + unbind: function( types, fn ) { + return this.off( types, null, fn ); + }, + + live: function( types, data, fn ) { + jQuery( this.context ).on( types, this.selector, data, fn ); + return this; + }, + die: function( types, fn ) { + jQuery( this.context ).off( types, this.selector || "**", fn ); + return this; + }, + + delegate: function( selector, types, data, fn ) { + return this.on( types, selector, data, fn ); + }, + undelegate: function( selector, types, fn ) { + // ( namespace ) or ( selector, types [, fn] ) + return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn ); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + if ( this[0] ) { + return jQuery.event.trigger( type, data, this[0], true ); + } + }, + + toggle: function( fn ) { + // Save reference to arguments for access in closure + var args = arguments, + guid = fn.guid || jQuery.guid++, + i = 0, + toggler = function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + }; + + // link all the functions, so any of them can unbind this click handler + toggler.guid = guid; + while ( i < args.length ) { + args[ i++ ].guid = guid; + } + + return this.click( toggler ); + }, + + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +}); + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.on( name, null, data, fn ) : + this.trigger( name ); + }; + + if ( jQuery.attrFn ) { + jQuery.attrFn[ name ] = true; + } + + if ( rkeyEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks; + } + + if ( rmouseEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; + } +}); + + + +/*! + * Sizzle CSS Selector Engine + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + expando = "sizcache" + (Math.random() + '').replace('.', ''), + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true, + rBackslash = /\\/g, + rReturn = /\r\n/g, + rNonWord = /\W/; + +// Here we check if the JavaScript engine is using some sort of +// optimization where it does not always call our comparision +// function. If that is the case, discard the hasDuplicate value. +// Thus far that includes Google Chrome. +[0, 0].sort(function() { + baseHasDuplicate = false; + return 0; +}); + +var Sizzle = function( selector, context, results, seed ) { + results = results || []; + context = context || document; + + var origContext = context; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var m, set, checkSet, extra, ret, cur, pop, i, + prune = true, + contextXML = Sizzle.isXML( context ), + parts = [], + soFar = selector; + + // Reset the position of the chunker regexp (start from head) + do { + chunker.exec( "" ); + m = chunker.exec( soFar ); + + if ( m ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + } while ( m ); + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context, seed ); + + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) { + selector += parts.shift(); + } + + set = posProcess( selector, set, seed ); + } + } + + } else { + // Take a shortcut and set the context if the root selector is an ID + // (but not if it'll be faster if the inner selector is an ID) + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + + ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? + Sizzle.filter( ret.expr, ret.set )[0] : + ret.set[0]; + } + + if ( context ) { + ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + + set = ret.expr ? + Sizzle.filter( ret.expr, ret.set ) : + ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray( set ); + + } else { + prune = false; + } + + while ( parts.length ) { + cur = parts.pop(); + pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + Sizzle.error( cur || selector ); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + + } else if ( context && context.nodeType === 1 ) { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + + } else { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function( results ) { + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[ i - 1 ] ) { + results.splice( i--, 1 ); + } + } + } + } + + return results; +}; + +Sizzle.matches = function( expr, set ) { + return Sizzle( expr, null, null, set ); +}; + +Sizzle.matchesSelector = function( node, expr ) { + return Sizzle( expr, null, null, [node] ).length > 0; +}; + +Sizzle.find = function( expr, context, isXML ) { + var set, i, len, match, type, left; + + if ( !expr ) { + return []; + } + + for ( i = 0, len = Expr.order.length; i < len; i++ ) { + type = Expr.order[i]; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + left = match[1]; + match.splice( 1, 1 ); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace( rBackslash, "" ); + set = Expr.find[ type ]( match, context, isXML ); + + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( "*" ) : + []; + } + + return { set: set, expr: expr }; +}; + +Sizzle.filter = function( expr, set, inplace, not ) { + var match, anyFound, + type, found, item, filter, left, + i, pass, + old = expr, + result = [], + curLoop = set, + isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); + + while ( expr && set.length ) { + for ( type in Expr.filter ) { + if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { + filter = Expr.filter[ type ]; + left = match[1]; + + anyFound = false; + + match.splice(1,1); + + if ( left.substr( left.length - 1 ) === "\\" ) { + continue; + } + + if ( curLoop === result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + pass = not ^ found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + + } else { + curLoop[i] = false; + } + + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + // Improper expression + if ( expr === old ) { + if ( anyFound == null ) { + Sizzle.error( expr ); + + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Utility function for retreiving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +var getText = Sizzle.getText = function( elem ) { + var i, node, + nodeType = elem.nodeType, + ret = ""; + + if ( nodeType ) { + if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent || innerText for elements + if ( typeof elem.textContent === 'string' ) { + return elem.textContent; + } else if ( typeof elem.innerText === 'string' ) { + // Replace IE's carriage returns + return elem.innerText.replace( rReturn, '' ); + } else { + // Traverse it's children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + } else { + + // If no nodeType, this is expected to be an array + for ( i = 0; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + if ( node.nodeType !== 8 ) { + ret += getText( node ); + } + } + } + return ret; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + + match: { + ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ + }, + + leftMatch: {}, + + attrMap: { + "class": "className", + "for": "htmlFor" + }, + + attrHandle: { + href: function( elem ) { + return elem.getAttribute( "href" ); + }, + type: function( elem ) { + return elem.getAttribute( "type" ); + } + }, + + relative: { + "+": function(checkSet, part){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !rNonWord.test( part ), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag ) { + part = part.toLowerCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + + ">": function( checkSet, part ) { + var elem, + isPartStr = typeof part === "string", + i = 0, + l = checkSet.length; + + if ( isPartStr && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } + + } else { + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + + "": function(checkSet, part, isXML){ + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); + }, + + "~": function( checkSet, part, isXML ) { + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); + } + }, + + find: { + ID: function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }, + + NAME: function( match, context ) { + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], + results = context.getElementsByName( match[1] ); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + + TAG: function( match, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( match[1] ); + } + } + }, + preFilter: { + CLASS: function( match, curLoop, inplace, result, not, isXML ) { + match = " " + match[1].replace( rBackslash, "" ) + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { + if ( !inplace ) { + result.push( elem ); + } + + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + + ID: function( match ) { + return match[1].replace( rBackslash, "" ); + }, + + TAG: function( match, curLoop ) { + return match[1].replace( rBackslash, "" ).toLowerCase(); + }, + + CHILD: function( match ) { + if ( match[1] === "nth" ) { + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + match[2] = match[2].replace(/^\+|\s*/g, ''); + + // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' + var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( + match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + // calculate the numbers (first)n+(last) including if they are negative + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + // TODO: Move to normal caching system + match[0] = done++; + + return match; + }, + + ATTR: function( match, curLoop, inplace, result, not, isXML ) { + var name = match[1] = match[1].replace( rBackslash, "" ); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + // Handle if an un-quoted value was used + match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + + PSEUDO: function( match, curLoop, inplace, result, not ) { + if ( match[1] === "not" ) { + // If we're dealing with a complex expression, or a simple one + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + + if ( !inplace ) { + result.push.apply( result, ret ); + } + + return false; + } + + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + + POS: function( match ) { + match.unshift( true ); + + return match; + } + }, + + filters: { + enabled: function( elem ) { + return elem.disabled === false && elem.type !== "hidden"; + }, + + disabled: function( elem ) { + return elem.disabled === true; + }, + + checked: function( elem ) { + return elem.checked === true; + }, + + selected: function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + parent: function( elem ) { + return !!elem.firstChild; + }, + + empty: function( elem ) { + return !elem.firstChild; + }, + + has: function( elem, i, match ) { + return !!Sizzle( match[3], elem ).length; + }, + + header: function( elem ) { + return (/h\d/i).test( elem.nodeName ); + }, + + text: function( elem ) { + var attr = elem.getAttribute( "type" ), type = elem.type; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); + }, + + radio: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; + }, + + checkbox: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; + }, + + file: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; + }, + + password: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; + }, + + submit: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "submit" === elem.type; + }, + + image: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; + }, + + reset: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "reset" === elem.type; + }, + + button: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && "button" === elem.type || name === "button"; + }, + + input: function( elem ) { + return (/input|select|textarea|button/i).test( elem.nodeName ); + }, + + focus: function( elem ) { + return elem === elem.ownerDocument.activeElement; + } + }, + setFilters: { + first: function( elem, i ) { + return i === 0; + }, + + last: function( elem, i, match, array ) { + return i === array.length - 1; + }, + + even: function( elem, i ) { + return i % 2 === 0; + }, + + odd: function( elem, i ) { + return i % 2 === 1; + }, + + lt: function( elem, i, match ) { + return i < match[3] - 0; + }, + + gt: function( elem, i, match ) { + return i > match[3] - 0; + }, + + nth: function( elem, i, match ) { + return match[3] - 0 === i; + }, + + eq: function( elem, i, match ) { + return match[3] - 0 === i; + } + }, + filter: { + PSEUDO: function( elem, match, i, array ) { + var name = match[1], + filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0; + + } else if ( name === "not" ) { + var not = match[3]; + + for ( var j = 0, l = not.length; j < l; j++ ) { + if ( not[j] === elem ) { + return false; + } + } + + return true; + + } else { + Sizzle.error( name ); + } + }, + + CHILD: function( elem, match ) { + var first, last, + doneName, parent, cache, + count, diff, + type = match[1], + node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + /* falls through */ + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + + case "nth": + first = match[2]; + last = match[3]; + + if ( first === 1 && last === 0 ) { + return true; + } + + doneName = match[0]; + parent = elem.parentNode; + + if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) { + count = 0; + + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + + parent[ expando ] = doneName; + } + + diff = elem.nodeIndex - last; + + if ( first === 0 ) { + return diff === 0; + + } else { + return ( diff % first === 0 && diff / first >= 0 ); + } + } + }, + + ID: function( elem, match ) { + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + + TAG: function( elem, match ) { + return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match; + }, + + CLASS: function( elem, match ) { + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + + ATTR: function( elem, match ) { + var name = match[1], + result = Sizzle.attr ? + Sizzle.attr( elem, name ) : + Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + !type && Sizzle.attr ? + result != null : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value !== check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + + POS: function( elem, match, i, array ) { + var name = match[2], + filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS, + fescape = function(all, num){ + return "\\" + (num - 0 + 1); + }; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); +} +// Expose origPOS +// "global" as in regardless of relation to brackets/parens +Expr.match.globalPOS = origPOS; + +var makeArray = function( array, results ) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +// Perform a simple check to determine if the browser is capable of +// converting a NodeList to an array using builtin methods. +// Also verifies that the returned array holds DOM nodes +// (which is not the case in the Blackberry browser) +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; + +// Provide a fallback method if it does not work +} catch( e ) { + makeArray = function( array, results ) { + var i = 0, + ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + + } else { + if ( typeof array.length === "number" ) { + for ( var l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + + } else { + for ( ; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder, siblingCheck; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + return a.compareDocumentPosition ? -1 : 1; + } + + return a.compareDocumentPosition(b) & 4 ? -1 : 1; + }; + +} else { + sortOrder = function( a, b ) { + // The nodes are identical, we can exit early + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Fallback to using sourceIndex (in IE) if it's available on both nodes + } else if ( a.sourceIndex && b.sourceIndex ) { + return a.sourceIndex - b.sourceIndex; + } + + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + // If the nodes are siblings (or identical) we can do a quick check + if ( aup === bup ) { + return siblingCheck( a, b ); + + // If no parents were found then the nodes are disconnected + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + // Otherwise they're somewhere else in the tree so we need + // to build up a full list of the parentNodes for comparison + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + // Start walking down the tree looking for a discrepancy + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + // We ended someplace up the tree so do a sibling check + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + + siblingCheck = function( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; + }; +} + +// Check to see if the browser returns elements by name when +// querying by getElementById (and provide a workaround) +(function(){ + // We're going to inject a fake input element with a specified name + var form = document.createElement("div"), + id = "script" + (new Date()).getTime(), + root = document.documentElement; + + form.innerHTML = ""; + + // Inject it into the root element, check its status, and remove it quickly + root.insertBefore( form, root.firstChild ); + + // The workaround has to do additional checks after a getElementById + // Which slows things down for other browsers (hence the branching) + if ( document.getElementById( id ) ) { + Expr.find.ID = function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + + return m ? + m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? + [m] : + undefined : + []; + } + }; + + Expr.filter.ID = function( elem, match ) { + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + + // release memory in IE + root = form = null; +})(); + +(function(){ + // Check to see if the browser returns only elements + // when doing getElementsByTagName("*") + + // Create a fake element + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + // Make sure no comments are found + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function( match, context ) { + var results = context.getElementsByTagName( match[1] ); + + // Filter out possible comments + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + // Check to see if an attribute returns normalized href attributes + div.innerHTML = ""; + + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + + Expr.attrHandle.href = function( elem ) { + return elem.getAttribute( "href", 2 ); + }; + } + + // release memory in IE + div = null; +})(); + +if ( document.querySelectorAll ) { + (function(){ + var oldSizzle = Sizzle, + div = document.createElement("div"), + id = "__sizzle__"; + + div.innerHTML = "

    "; + + // Safari can't handle uppercase or unicode characters when + // in quirks mode. + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function( query, context, extra, seed ) { + context = context || document; + + // Only use querySelectorAll on non-XML documents + // (ID selectors don't work in non-HTML documents) + if ( !seed && !Sizzle.isXML(context) ) { + // See if we find a selector to speed up + var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); + + if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { + // Speed-up: Sizzle("TAG") + if ( match[1] ) { + return makeArray( context.getElementsByTagName( query ), extra ); + + // Speed-up: Sizzle(".CLASS") + } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { + return makeArray( context.getElementsByClassName( match[2] ), extra ); + } + } + + if ( context.nodeType === 9 ) { + // Speed-up: Sizzle("body") + // The body element only exists once, optimize finding it + if ( query === "body" && context.body ) { + return makeArray( [ context.body ], extra ); + + // Speed-up: Sizzle("#ID") + } else if ( match && match[3] ) { + var elem = context.getElementById( match[3] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id === match[3] ) { + return makeArray( [ elem ], extra ); + } + + } else { + return makeArray( [], extra ); + } + } + + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(qsaError) {} + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + var oldContext = context, + old = context.getAttribute( "id" ), + nid = old || id, + hasParent = context.parentNode, + relativeHierarchySelector = /^\s*[+~]/.test( query ); + + if ( !old ) { + context.setAttribute( "id", nid ); + } else { + nid = nid.replace( /'/g, "\\$&" ); + } + if ( relativeHierarchySelector && hasParent ) { + context = context.parentNode; + } + + try { + if ( !relativeHierarchySelector || hasParent ) { + return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); + } + + } catch(pseudoError) { + } finally { + if ( !old ) { + oldContext.removeAttribute( "id" ); + } + } + } + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + // release memory in IE + div = null; + })(); +} + +(function(){ + var html = document.documentElement, + matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; + + if ( matches ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9 fails this) + var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), + pseudoWorks = false; + + try { + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( document.documentElement, "[test!='']:sizzle" ); + + } catch( pseudoError ) { + pseudoWorks = true; + } + + Sizzle.matchesSelector = function( node, expr ) { + // Make sure that attribute selectors are quoted + expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); + + if ( !Sizzle.isXML( node ) ) { + try { + if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { + var ret = matches.call( node, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || !disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9, so check for that + node.document && node.document.nodeType !== 11 ) { + return ret; + } + } + } catch(e) {} + } + + return Sizzle(expr, null, null, [node]).length > 0; + }; + } +})(); + +(function(){ + var div = document.createElement("div"); + + div.innerHTML = "
    "; + + // Opera can't find a second classname (in 9.6) + // Also, make sure that getElementsByClassName actually exists + if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { + return; + } + + // Safari caches class attributes, doesn't catch changes (in 3.2) + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) { + return; + } + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function( match, context, isXML ) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + // release memory in IE + div = null; +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem[ expando ] === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem[ expando ] = doneName; + elem.sizset = i; + } + + if ( elem.nodeName.toLowerCase() === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem[ expando ] === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem[ expando ] = doneName; + elem.sizset = i; + } + + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +if ( document.documentElement.contains ) { + Sizzle.contains = function( a, b ) { + return a !== b && (a.contains ? a.contains(b) : true); + }; + +} else if ( document.documentElement.compareDocumentPosition ) { + Sizzle.contains = function( a, b ) { + return !!(a.compareDocumentPosition(b) & 16); + }; + +} else { + Sizzle.contains = function() { + return false; + }; +} + +Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; + + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +var posProcess = function( selector, context, seed ) { + var match, + tmpSet = [], + later = "", + root = context.nodeType ? [context] : context; + + // Position selectors must be done after the filter + // And so must :not(positional) so we move all PSEUDOs to the end + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet, seed ); + } + + return Sizzle.filter( later, tmpSet ); +}; + +// EXPOSE +// Override sizzle attribute retrieval +Sizzle.attr = jQuery.attr; +Sizzle.selectors.attrMap = {}; +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.filters; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})(); + + +var runtil = /Until$/, + rparentsprev = /^(?:parents|prevUntil|prevAll)/, + // Note: This RegExp should be improved, or likely pulled from Sizzle + rmultiselector = /,/, + isSimple = /^.[^:#\[\.,]*$/, + slice = Array.prototype.slice, + POS = jQuery.expr.match.globalPOS, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var self = this, + i, l; + + if ( typeof selector !== "string" ) { + return jQuery( selector ).filter(function() { + for ( i = 0, l = self.length; i < l; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }); + } + + var ret = this.pushStack( "", "find", selector ), + length, n, r; + + for ( i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + jQuery.find( selector, this[i], ret ); + + if ( i > 0 ) { + // Make sure that the results are unique + for ( n = length; n < ret.length; n++ ) { + for ( r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return ret; + }, + + has: function( target ) { + var targets = jQuery( target ); + return this.filter(function() { + for ( var i = 0, l = targets.length; i < l; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false), "not", selector); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }, + + is: function( selector ) { + return !!selector && ( + typeof selector === "string" ? + // If this is a positional selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + POS.test( selector ) ? + jQuery( selector, this.context ).index( this[0] ) >= 0 : + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }, + + closest: function( selectors, context ) { + var ret = [], i, l, cur = this[0]; + + // Array (deprecated as of jQuery 1.7) + if ( jQuery.isArray( selectors ) ) { + var level = 1; + + while ( cur && cur.ownerDocument && cur !== context ) { + for ( i = 0; i < selectors.length; i++ ) { + + if ( jQuery( cur ).is( selectors[ i ] ) ) { + ret.push({ selector: selectors[ i ], elem: cur, level: level }); + } + } + + cur = cur.parentNode; + level++; + } + + return ret; + } + + // String + var pos = POS.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( i = 0, l = this.length; i < l; i++ ) { + cur = this[i]; + + while ( cur ) { + if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { + ret.push( cur ); + break; + + } else { + cur = cur.parentNode; + if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) { + break; + } + } + } + } + + ret = ret.length > 1 ? jQuery.unique( ret ) : ret; + + return this.pushStack( ret, "closest", selectors ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? + all : + jQuery.unique( all ) ); + }, + + andSelf: function() { + return this.add( this.prevObject ); + } +}); + +// A painfully simple check to see if an element is disconnected +// from a document (should be improved, where feasible). +function isDisconnected( node ) { + return !node || !node.parentNode || node.parentNode.nodeType === 11; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return jQuery.nth( elem, 2, "nextSibling" ); + }, + prev: function( elem ) { + return jQuery.nth( elem, 2, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.makeArray( elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; + + if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret, name, slice.call( arguments ).join(",") ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 ? + jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : + jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + nth: function( cur, result, dir, elem ) { + result = result || 1; + var num = 0; + + for ( ; cur; cur = cur[dir] ) { + if ( cur.nodeType === 1 && ++num === result ) { + break; + } + } + + return cur; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, keep ) { + + // Can't pass null or undefined to indexOf in Firefox 4 + // Set to 0 to skip string check + qualifier = qualifier || 0; + + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + var retVal = !!qualifier.call( elem, i, elem ); + return retVal === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem, i ) { + return ( elem === qualifier ) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem, i ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; + }); +} + + + + +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, + rtagName = /<([\w:]+)/, + rtbody = /]", "i"), + // checked="checked" or checked + rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, + rscriptType = /\/(java|ecma)script/i, + rcleanScript = /^\s*", "" ], + legend: [ 1, "
    ", "
    " ], + thead: [ 1, "", "
    " ], + tr: [ 2, "", "
    " ], + td: [ 3, "", "
    " ], + col: [ 2, "", "
    " ], + area: [ 1, "", "" ], + _default: [ 0, "", "" ] + }, + safeFragment = createSafeFragment( document ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// IE can't serialize and + + + + + diff --git a/GRID/jquery.gridder.html b/GRID/jquery.gridder.html new file mode 100644 index 0000000..a2b41d3 --- /dev/null +++ b/GRID/jquery.gridder.html @@ -0,0 +1,46 @@ + + + + + + + +
    +
      +
      + + + + + + + diff --git a/GRID/jquery.gridder_test.js b/GRID/jquery.gridder_test.js new file mode 100644 index 0000000..2df25e7 --- /dev/null +++ b/GRID/jquery.gridder_test.js @@ -0,0 +1,38 @@ +/*global QUnit:false, module:false, test:false, asyncTest:false, expect:false*/ +/*global start:false, stop:false ok:false, equal:false, notEqual:false, deepEqual:false*/ +/*global notDeepEqual:false, strictEqual:false, notStrictEqual:false, raises:false*/ +(function($) { + + /* + ======== A Handy Little QUnit Reference ======== + http://docs.jquery.com/QUnit + + Test methods: + expect(numAssertions) + stop(increment) + start(decrement) + Test assertions: + ok(value, [message]) + equal(actual, expected, [message]) + notEqual(actual, expected, [message]) + deepEqual(actual, expected, [message]) + notDeepEqual(actual, expected, [message]) + strictEqual(actual, expected, [message]) + notStrictEqual(actual, expected, [message]) + raises(block, [expected], [message]) + */ + + module('jQuery#gridster', { + setup: function() { + + this.el = $('#qunit-fixture').find(".wrapper ul"); + + } + }); + + // test('is chainable', 1, function() { + // // Not a bad test to run on collection methods. + // strictEqual(this.el, this.el.gridster(), 'should be chaninable'); + // }); + +}(jQuery)); diff --git a/GRID/package.json b/GRID/package.json new file mode 100644 index 0000000..23354b5 --- /dev/null +++ b/GRID/package.json @@ -0,0 +1,41 @@ +{ + "name": "gridster", + "title": "gridster.js", + "description": "a drag-and-drop multi-column jQuery grid plugin", + "version": "0.5.6", + "homepage": "http://gridster.net/", + "author": { + "name": "ducksboard", + "email": "hackers@ducksboard.com" + }, + "repository": { + "type": "git", + "url": "git://github.com/ducksboard/gridster.js.git" + }, + "bugs": { + "url": "https://github.com/ducksboard/gridster.js/issues" + }, + "licenses": [ + { + "type": "MIT", + "url": "https://github.com/ducksboard/gridster.js/blob/master/LICENSE" + } + ], + "keywords": [], + "dependencies": { + "jquery": "git+https://github.com/jquery/jquery.git#2.0.3" + }, + "devDependencies": { + "grunt": "~0.4.1", + "grunt-contrib-uglify": "~0.2.0", + "grunt-contrib-jshint": "~0.3.0", + "grunt-contrib-concat": "~0.1.3", + "grunt-contrib-watch": "~0.3.1", + "grunt-contrib-cssmin": "~0.5.0", + "grunt-contrib-yuidoc": "~0.4.0", + "bower": "~0.9.2", + "qunitjs": "~1.11.0", + "grunt-bump": "0.0.11", + "grunt-conventional-changelog": "~1.0.0" + } +} diff --git a/GRID/src/jquery.collision.js b/GRID/src/jquery.collision.js new file mode 100644 index 0000000..4a15e8e --- /dev/null +++ b/GRID/src/jquery.collision.js @@ -0,0 +1,242 @@ +/* + * jquery.collision + * https://github.com/ducksboard/gridster.js + * + * Copyright (c) 2012 ducksboard + * Licensed under the MIT licenses. + */ + +;(function(root, factory) { + + if (typeof define === 'function' && define.amd) { + define('gridster-collision', ['jquery', 'gridster-coords'], factory); + } else { + root.GridsterCollision = factory(root.$ || root.jQuery, + root.GridsterCoords); + } + +}(this, function($, Coords) { + + var defaults = { + colliders_context: document.body, + overlapping_region: 'C' + // ,on_overlap: function(collider_data){}, + // on_overlap_start : function(collider_data){}, + // on_overlap_stop : function(collider_data){} + }; + + + /** + * Detects collisions between a DOM element against other DOM elements or + * Coords objects. + * + * @class Collision + * @uses Coords + * @param {HTMLElement} el The jQuery wrapped HTMLElement. + * @param {HTMLElement|Array} colliders Can be a jQuery collection + * of HTMLElements or an Array of Coords instances. + * @param {Object} [options] An Object with all options you want to + * overwrite: + * @param {String} [options.overlapping_region] Determines when collision + * is valid, depending on the overlapped area. Values can be: 'N', 'S', + * 'W', 'E', 'C' or 'all'. Default is 'C'. + * @param {Function} [options.on_overlap_start] Executes a function the first + * time each `collider ` is overlapped. + * @param {Function} [options.on_overlap_stop] Executes a function when a + * `collider` is no longer collided. + * @param {Function} [options.on_overlap] Executes a function when the + * mouse is moved during the collision. + * @return {Object} Collision instance. + * @constructor + */ + function Collision(el, colliders, options) { + this.options = $.extend(defaults, options); + this.$element = el; + this.last_colliders = []; + this.last_colliders_coords = []; + this.set_colliders(colliders); + + this.init(); + } + + Collision.defaults = defaults; + + var fn = Collision.prototype; + + + fn.init = function() { + this.find_collisions(); + }; + + + fn.overlaps = function(a, b) { + var x = false; + var y = false; + + if ((b.x1 >= a.x1 && b.x1 <= a.x2) || + (b.x2 >= a.x1 && b.x2 <= a.x2) || + (a.x1 >= b.x1 && a.x2 <= b.x2) + ) { x = true; } + + if ((b.y1 >= a.y1 && b.y1 <= a.y2) || + (b.y2 >= a.y1 && b.y2 <= a.y2) || + (a.y1 >= b.y1 && a.y2 <= b.y2) + ) { y = true; } + + return (x && y); + }; + + + fn.detect_overlapping_region = function(a, b){ + var regionX = ''; + var regionY = ''; + + if (a.y1 > b.cy && a.y1 < b.y2) { regionX = 'N'; } + if (a.y2 > b.y1 && a.y2 < b.cy) { regionX = 'S'; } + if (a.x1 > b.cx && a.x1 < b.x2) { regionY = 'W'; } + if (a.x2 > b.x1 && a.x2 < b.cx) { regionY = 'E'; } + + return (regionX + regionY) || 'C'; + }; + + + fn.calculate_overlapped_area_coords = function(a, b){ + var x1 = Math.max(a.x1, b.x1); + var y1 = Math.max(a.y1, b.y1); + var x2 = Math.min(a.x2, b.x2); + var y2 = Math.min(a.y2, b.y2); + + return $({ + left: x1, + top: y1, + width : (x2 - x1), + height: (y2 - y1) + }).coords().get(); + }; + + + fn.calculate_overlapped_area = function(coords){ + return (coords.width * coords.height); + }; + + + fn.manage_colliders_start_stop = function(new_colliders_coords, start_callback, stop_callback){ + var last = this.last_colliders_coords; + + for (var i = 0, il = last.length; i < il; i++) { + if ($.inArray(last[i], new_colliders_coords) === -1) { + start_callback.call(this, last[i]); + } + } + + for (var j = 0, jl = new_colliders_coords.length; j < jl; j++) { + if ($.inArray(new_colliders_coords[j], last) === -1) { + stop_callback.call(this, new_colliders_coords[j]); + } + + } + }; + + + fn.find_collisions = function(player_data_coords){ + var self = this; + var overlapping_region = this.options.overlapping_region; + var colliders_coords = []; + var colliders_data = []; + var $colliders = (this.colliders || this.$colliders); + var count = $colliders.length; + var player_coords = self.$element.coords() + .update(player_data_coords || false).get(); + + while(count--){ + var $collider = self.$colliders ? + $($colliders[count]) : $colliders[count]; + var $collider_coords_ins = ($collider.isCoords) ? + $collider : $collider.coords(); + var collider_coords = $collider_coords_ins.get(); + var overlaps = self.overlaps(player_coords, collider_coords); + + if (!overlaps) { + continue; + } + + var region = self.detect_overlapping_region( + player_coords, collider_coords); + + //todo: make this an option + if (region === overlapping_region || overlapping_region === 'all') { + + var area_coords = self.calculate_overlapped_area_coords( + player_coords, collider_coords); + var area = self.calculate_overlapped_area(area_coords); + var collider_data = { + area: area, + area_coords : area_coords, + region: region, + coords: collider_coords, + player_coords: player_coords, + el: $collider + }; + + if (self.options.on_overlap) { + self.options.on_overlap.call(this, collider_data); + } + colliders_coords.push($collider_coords_ins); + colliders_data.push(collider_data); + } + } + + if (self.options.on_overlap_stop || self.options.on_overlap_start) { + this.manage_colliders_start_stop(colliders_coords, + self.options.on_overlap_start, self.options.on_overlap_stop); + } + + this.last_colliders_coords = colliders_coords; + + return colliders_data; + }; + + + fn.get_closest_colliders = function(player_data_coords){ + var colliders = this.find_collisions(player_data_coords); + + colliders.sort(function(a, b) { + /* if colliders are being overlapped by the "C" (center) region, + * we have to set a lower index in the array to which they are placed + * above in the grid. */ + if (a.region === 'C' && b.region === 'C') { + if (a.coords.y1 < b.coords.y1 || a.coords.x1 < b.coords.x1) { + return - 1; + }else{ + return 1; + } + } + + if (a.area < b.area) { + return 1; + } + + return 1; + }); + return colliders; + }; + + + fn.set_colliders = function(colliders) { + if (typeof colliders === 'string' || colliders instanceof $) { + this.$colliders = $(colliders, + this.options.colliders_context).not(this.$element); + }else{ + this.colliders = $(colliders); + } + }; + + + //jQuery adapter + $.fn.collision = function(collider, options) { + return new Collision( this, collider, options ); + }; + + return Collision; + +})); diff --git a/GRID/src/jquery.coords.js b/GRID/src/jquery.coords.js new file mode 100644 index 0000000..821b67c --- /dev/null +++ b/GRID/src/jquery.coords.js @@ -0,0 +1,125 @@ +/* + * jquery.coords + * https://github.com/ducksboard/gridster.js + * + * Copyright (c) 2012 ducksboard + * Licensed under the MIT licenses. + */ + +;(function(root, factory) { + + if (typeof define === 'function' && define.amd) { + define('gridster-coords', ['jquery'], factory); + } else { + root.GridsterCoords = factory(root.$ || root.jQuery); + } + +}(this, function($) { + /** + * Creates objects with coordinates (x1, y1, x2, y2, cx, cy, width, height) + * to simulate DOM elements on the screen. + * Coords is used by Gridster to create a faux grid with any DOM element can + * collide. + * + * @class Coords + * @param {HTMLElement|Object} obj The jQuery HTMLElement or a object with: left, + * top, width and height properties. + * @return {Object} Coords instance. + * @constructor + */ + function Coords(obj) { + if (obj[0] && $.isPlainObject(obj[0])) { + this.data = obj[0]; + }else { + this.el = obj; + } + + this.isCoords = true; + this.coords = {}; + this.init(); + return this; + } + + + var fn = Coords.prototype; + + + fn.init = function(){ + this.set(); + this.original_coords = this.get(); + }; + + + fn.set = function(update, not_update_offsets) { + var el = this.el; + + if (el && !update) { + this.data = el.offset(); + this.data.width = el.width(); + this.data.height = el.height(); + } + + if (el && update && !not_update_offsets) { + var offset = el.offset(); + this.data.top = offset.top; + this.data.left = offset.left; + } + + var d = this.data; + + typeof d.left === 'undefined' && (d.left = d.x1); + typeof d.top === 'undefined' && (d.top = d.y1); + + this.coords.x1 = d.left; + this.coords.y1 = d.top; + this.coords.x2 = d.left + d.width; + this.coords.y2 = d.top + d.height; + this.coords.cx = d.left + (d.width / 2); + this.coords.cy = d.top + (d.height / 2); + this.coords.width = d.width; + this.coords.height = d.height; + this.coords.el = el || false ; + + return this; + }; + + + fn.update = function(data){ + if (!data && !this.el) { + return this; + } + + if (data) { + var new_data = $.extend({}, this.data, data); + this.data = new_data; + return this.set(true, true); + } + + this.set(true); + return this; + }; + + + fn.get = function(){ + return this.coords; + }; + + fn.destroy = function() { + this.el.removeData('coords'); + delete this.el; + }; + + //jQuery adapter + $.fn.coords = function() { + if (this.data('coords') ) { + return this.data('coords'); + } + + var ins = new Coords(this, arguments[0]); + this.data('coords', ins); + return ins; + }; + + return Coords; + +})); diff --git a/GRID/src/jquery.draggable.js b/GRID/src/jquery.draggable.js new file mode 100644 index 0000000..d9b5d98 --- /dev/null +++ b/GRID/src/jquery.draggable.js @@ -0,0 +1,432 @@ +/* + * jquery.draggable + * https://github.com/ducksboard/gridster.js + * + * Copyright (c) 2012 ducksboard + * Licensed under the MIT licenses. + */ + +;(function(root, factory) { + + if (typeof define === 'function' && define.amd) { + define('gridster-draggable', ['jquery'], factory); + } else { + root.GridsterDraggable = factory(root.$ || root.jQuery); + } + +}(this, function($) { + + var defaults = { + items: 'li', + distance: 1, + limit: true, + offset_left: 0, + autoscroll: true, + ignore_dragging: ['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON'], // or function + handle: null, + container_width: 0, // 0 == auto + move_element: true, + helper: false, // or 'clone' + remove_helper: true + // drag: function(e) {}, + // start : function(e, ui) {}, + // stop : function(e) {} + }; + + var $window = $(window); + var dir_map = { x : 'left', y : 'top' }; + var isTouch = !!('ontouchstart' in window); + + var capitalize = function(str) { + return str.charAt(0).toUpperCase() + str.slice(1); + }; + + var idCounter = 0; + var uniqId = function() { + return ++idCounter + ''; + } + + /** + * Basic drag implementation for DOM elements inside a container. + * Provide start/stop/drag callbacks. + * + * @class Draggable + * @param {HTMLElement} el The HTMLelement that contains all the widgets + * to be dragged. + * @param {Object} [options] An Object with all options you want to + * overwrite: + * @param {HTMLElement|String} [options.items] Define who will + * be the draggable items. Can be a CSS Selector String or a + * collection of HTMLElements. + * @param {Number} [options.distance] Distance in pixels after mousedown + * the mouse must move before dragging should start. + * @param {Boolean} [options.limit] Constrains dragging to the width of + * the container + * @param {Object|Function} [options.ignore_dragging] Array of node names + * that sould not trigger dragging, by default is `['INPUT', 'TEXTAREA', + * 'SELECT', 'BUTTON']`. If a function is used return true to ignore dragging. + * @param {offset_left} [options.offset_left] Offset added to the item + * that is being dragged. + * @param {Number} [options.drag] Executes a callback when the mouse is + * moved during the dragging. + * @param {Number} [options.start] Executes a callback when the drag + * starts. + * @param {Number} [options.stop] Executes a callback when the drag stops. + * @return {Object} Returns `el`. + * @constructor + */ + function Draggable(el, options) { + this.options = $.extend({}, defaults, options); + this.$document = $(document); + this.$container = $(el); + this.$dragitems = $(this.options.items, this.$container); + this.is_dragging = false; + this.player_min_left = 0 + this.options.offset_left; + this.id = uniqId(); + this.ns = '.gridster-draggable-' + this.id; + this.init(); + } + + Draggable.defaults = defaults; + + var fn = Draggable.prototype; + + fn.init = function() { + var pos = this.$container.css('position'); + this.calculate_dimensions(); + this.$container.css('position', pos === 'static' ? 'relative' : pos); + this.disabled = false; + this.events(); + + $(window).bind(this.nsEvent('resize'), + throttle($.proxy(this.calculate_dimensions, this), 200)); + }; + + fn.nsEvent = function(ev) { + return (ev || '') + this.ns; + }; + + fn.events = function() { + this.pointer_events = { + start: this.nsEvent('touchstart') + ' ' + this.nsEvent('mousedown'), + move: this.nsEvent('touchmove') + ' ' + this.nsEvent('mousemove'), + end: this.nsEvent('touchend') + ' ' + this.nsEvent('mouseup'), + }; + + this.$container.on(this.nsEvent('selectstart'), + $.proxy(this.on_select_start, this)); + + this.$container.on(this.pointer_events.start, this.options.items, + $.proxy(this.drag_handler, this)); + + this.$document.on(this.pointer_events.end, $.proxy(function(e) { + this.is_dragging = false; + if (this.disabled) { return; } + this.$document.off(this.pointer_events.move); + if (this.drag_start) { + this.on_dragstop(e); + } + }, this)); + }; + + fn.get_actual_pos = function($el) { + var pos = $el.position(); + return pos; + }; + + + fn.get_mouse_pos = function(e) { + if (e.originalEvent && e.originalEvent.touches) { + var oe = e.originalEvent; + e = oe.touches.length ? oe.touches[0] : oe.changedTouches[0]; + } + + return { + left: e.clientX, + top: e.clientY + }; + }; + + + fn.get_offset = function(e) { + e.preventDefault(); + var mouse_actual_pos = this.get_mouse_pos(e); + var diff_x = Math.round( + mouse_actual_pos.left - this.mouse_init_pos.left); + var diff_y = Math.round(mouse_actual_pos.top - this.mouse_init_pos.top); + + var left = Math.round(this.el_init_offset.left + + diff_x - this.baseX + $(window).scrollLeft() - this.win_offset_x); + var top = Math.round(this.el_init_offset.top + + diff_y - this.baseY + $(window).scrollTop() - this.win_offset_y); + + if (this.options.limit) { + if (left > this.player_max_left) { + left = this.player_max_left; + } else if(left < this.player_min_left) { + left = this.player_min_left; + } + } + + return { + position: { + left: left, + top: top + }, + pointer: { + left: mouse_actual_pos.left, + top: mouse_actual_pos.top, + diff_left: diff_x + ($(window).scrollLeft() - this.win_offset_x), + diff_top: diff_y + ($(window).scrollTop() - this.win_offset_y) + } + }; + }; + + + fn.get_drag_data = function(e) { + var offset = this.get_offset(e); + offset.$player = this.$player; + offset.$helper = this.helper ? this.$helper : this.$player; + + return offset; + }; + + + fn.set_limits = function(container_width) { + container_width || (container_width = this.$container.width()); + this.player_max_left = (container_width - this.player_width + + - this.options.offset_left); + + this.options.container_width = container_width; + + return this; + }; + + + fn.scroll_in = function(axis, data) { + var dir_prop = dir_map[axis]; + + var area_size = 50; + var scroll_inc = 30; + + var is_x = axis === 'x'; + var window_size = is_x ? this.window_width : this.window_height; + var doc_size = is_x ? $(document).width() : $(document).height(); + var player_size = is_x ? this.$player.width() : this.$player.height(); + + var next_scroll; + var scroll_offset = $window['scroll' + capitalize(dir_prop)](); + var min_window_pos = scroll_offset; + var max_window_pos = min_window_pos + window_size; + + var mouse_next_zone = max_window_pos - area_size; // down/right + var mouse_prev_zone = min_window_pos + area_size; // up/left + + var abs_mouse_pos = min_window_pos + data.pointer[dir_prop]; + + var max_player_pos = (doc_size - window_size + player_size); + + if (abs_mouse_pos >= mouse_next_zone) { + next_scroll = scroll_offset + scroll_inc; + if (next_scroll < max_player_pos) { + $window['scroll' + capitalize(dir_prop)](next_scroll); + this['scroll_offset_' + axis] += scroll_inc; + } + } + + if (abs_mouse_pos <= mouse_prev_zone) { + next_scroll = scroll_offset - scroll_inc; + if (next_scroll > 0) { + $window['scroll' + capitalize(dir_prop)](next_scroll); + this['scroll_offset_' + axis] -= scroll_inc; + } + } + + return this; + }; + + + fn.manage_scroll = function(data) { + this.scroll_in('x', data); + this.scroll_in('y', data); + }; + + + fn.calculate_dimensions = function(e) { + this.window_height = $window.height(); + this.window_width = $window.width(); + }; + + + fn.drag_handler = function(e) { + var node = e.target.nodeName; + // skip if drag is disabled, or click was not done with the mouse primary button + if (this.disabled || e.which !== 1 && !isTouch) { + return; + } + + if (this.ignore_drag(e)) { + return; + } + + var self = this; + var first = true; + this.$player = $(e.currentTarget); + + this.el_init_pos = this.get_actual_pos(this.$player); + this.mouse_init_pos = this.get_mouse_pos(e); + this.offsetY = this.mouse_init_pos.top - this.el_init_pos.top; + + this.$document.on(this.pointer_events.move, function(mme) { + var mouse_actual_pos = self.get_mouse_pos(mme); + var diff_x = Math.abs( + mouse_actual_pos.left - self.mouse_init_pos.left); + var diff_y = Math.abs( + mouse_actual_pos.top - self.mouse_init_pos.top); + if (!(diff_x > self.options.distance || + diff_y > self.options.distance) + ) { + return false; + } + + if (first) { + first = false; + self.on_dragstart.call(self, mme); + return false; + } + + if (self.is_dragging === true) { + self.on_dragmove.call(self, mme); + } + + return false; + }); + + if (!isTouch) { return false; } + }; + + + fn.on_dragstart = function(e) { + e.preventDefault(); + + if (this.is_dragging) { return this; } + + this.drag_start = this.is_dragging = true; + var offset = this.$container.offset(); + this.baseX = Math.round(offset.left); + this.baseY = Math.round(offset.top); + this.initial_container_width = this.options.container_width || this.$container.width(); + + if (this.options.helper === 'clone') { + this.$helper = this.$player.clone() + .appendTo(this.$container).addClass('helper'); + this.helper = true; + } else { + this.helper = false; + } + + this.win_offset_y = $(window).scrollTop(); + this.win_offset_x = $(window).scrollLeft(); + this.scroll_offset_y = 0; + this.scroll_offset_x = 0; + this.el_init_offset = this.$player.offset(); + this.player_width = this.$player.width(); + this.player_height = this.$player.height(); + + this.set_limits(this.options.container_width); + + if (this.options.start) { + this.options.start.call(this.$player, e, this.get_drag_data(e)); + } + return false; + }; + + + fn.on_dragmove = function(e) { + var data = this.get_drag_data(e); + + this.options.autoscroll && this.manage_scroll(data); + + if (this.options.move_element) { + (this.helper ? this.$helper : this.$player).css({ + 'position': 'absolute', + 'left' : data.position.left, + 'top' : data.position.top + }); + } + + var last_position = this.last_position || data.position; + data.prev_position = last_position; + + if (this.options.drag) { + this.options.drag.call(this.$player, e, data); + } + + this.last_position = data.position; + return false; + }; + + + fn.on_dragstop = function(e) { + var data = this.get_drag_data(e); + this.drag_start = false; + + if (this.options.stop) { + this.options.stop.call(this.$player, e, data); + } + + if (this.helper && this.options.remove_helper) { + this.$helper.remove(); + } + + return false; + }; + + fn.on_select_start = function(e) { + if (this.disabled) { return; } + + if (this.ignore_drag(e)) { + return; + } + + return false; + }; + + fn.enable = function() { + this.disabled = false; + }; + + fn.disable = function() { + this.disabled = true; + }; + + fn.destroy = function() { + this.disable(); + + this.$container.off(this.ns); + this.$document.off(this.ns); + $(window).off(this.ns); + + $.removeData(this.$container, 'drag'); + }; + + fn.ignore_drag = function(event) { + if (this.options.handle) { + return !$(event.target).is(this.options.handle); + } + + if ($.isFunction(this.options.ignore_dragging)) { + return this.options.ignore_dragging(event); + } + + return $(event.target).is(this.options.ignore_dragging.join(', ')); + }; + + //jQuery adapter + $.fn.drag = function ( options ) { + return new Draggable(this, options); + }; + + return Draggable; + +})); diff --git a/GRID/src/jquery.gridster.css b/GRID/src/jquery.gridster.css new file mode 100644 index 0000000..52feaab --- /dev/null +++ b/GRID/src/jquery.gridster.css @@ -0,0 +1,117 @@ +.gridster { + position:relative; +} + +.gridster > * { + margin: 0 auto; + -webkit-transition: height .4s, width .4s; + -moz-transition: height .4s, width .4s; + -o-transition: height .4s, width .4s; + -ms-transition: height .4s, width .4s; + transition: height .4s, width .4s; +} + +.gridster .gs-w { + z-index: 2; + position: absolute; +} + +.ready .gs-w:not(.preview-holder) { + -webkit-transition: opacity .3s, left .3s, top .3s; + -moz-transition: opacity .3s, left .3s, top .3s; + -o-transition: opacity .3s, left .3s, top .3s; + transition: opacity .3s, left .3s, top .3s; +} + +.ready .gs-w:not(.preview-holder), +.ready .resize-preview-holder { + -webkit-transition: opacity .3s, left .3s, top .3s, width .3s, height .3s; + -moz-transition: opacity .3s, left .3s, top .3s, width .3s, height .3s; + -o-transition: opacity .3s, left .3s, top .3s, width .3s, height .3s; + transition: opacity .3s, left .3s, top .3s, width .3s, height .3s; +} + +.gridster .preview-holder { + z-index: 1; + position: absolute; + background-color: #fff; + border-color: #fff; + opacity: 0.3; +} + +.gridster .player-revert { + z-index: 10!important; + -webkit-transition: left .3s, top .3s!important; + -moz-transition: left .3s, top .3s!important; + -o-transition: left .3s, top .3s!important; + transition: left .3s, top .3s!important; +} + +.gridster .dragging, +.gridster .resizing { + z-index: 10!important; + -webkit-transition: all 0s !important; + -moz-transition: all 0s !important; + -o-transition: all 0s !important; + transition: all 0s !important; +} + + +.gs-resize-handle { + position: absolute; + z-index: 1; +} + +.gs-resize-handle-both { + width: 20px; + height: 20px; + bottom: -8px; + right: -8px; + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pg08IS0tIEdlbmVyYXRvcjogQWRvYmUgRmlyZXdvcmtzIENTNiwgRXhwb3J0IFNWRyBFeHRlbnNpb24gYnkgQWFyb24gQmVhbGwgKGh0dHA6Ly9maXJld29ya3MuYWJlYWxsLmNvbSkgLiBWZXJzaW9uOiAwLjYuMSAgLS0+DTwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DTxzdmcgaWQ9IlVudGl0bGVkLVBhZ2UlMjAxIiB2aWV3Qm94PSIwIDAgNiA2IiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojZmZmZmZmMDAiIHZlcnNpb249IjEuMSINCXhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbDpzcGFjZT0icHJlc2VydmUiDQl4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjZweCIgaGVpZ2h0PSI2cHgiDT4NCTxnIG9wYWNpdHk9IjAuMzAyIj4NCQk8cGF0aCBkPSJNIDYgNiBMIDAgNiBMIDAgNC4yIEwgNCA0LjIgTCA0LjIgNC4yIEwgNC4yIDAgTCA2IDAgTCA2IDYgTCA2IDYgWiIgZmlsbD0iIzAwMDAwMCIvPg0JPC9nPg08L3N2Zz4='); + background-position: top left; + background-repeat: no-repeat; + cursor: se-resize; + z-index: 20; +} + +.gs-resize-handle-x { + top: 0; + bottom: 13px; + right: -5px; + width: 10px; + cursor: e-resize; +} + +.gs-resize-handle-y { + left: 0; + right: 13px; + bottom: -5px; + height: 10px; + cursor: s-resize; +} + +.gs-w:hover .gs-resize-handle, +.resizing .gs-resize-handle { + opacity: 1; +} + +.gs-resize-handle, +.gs-w.dragging .gs-resize-handle { + opacity: 0; +} + +.gs-resize-disabled .gs-resize-handle { + display: none!important; +} + +[data-max-sizex="1"] .gs-resize-handle-x, +[data-max-sizey="1"] .gs-resize-handle-y, +[data-max-sizey="1"][data-max-sizex="1"] .gs-resize-handle { + display: none !important; +} + +/* Uncomment this if you set helper : "clone" in draggable options */ +/*.gridster .player { + opacity:0; +} +*/ \ No newline at end of file diff --git a/GRID/src/jquery.gridster.extras.js b/GRID/src/jquery.gridster.extras.js new file mode 100644 index 0000000..e7ea59f --- /dev/null +++ b/GRID/src/jquery.gridster.extras.js @@ -0,0 +1,175 @@ +;(function(root, factory) { + + if (typeof define === 'function' && define.amd) { + define(['jquery', 'gridster'], factory); + } else { + root.Gridster = factory(root.$ || root.jQuery, root.Gridster); + } + +}(this, function($, Gridster) { + + var fn = Gridster.prototype; + + fn.widgets_in_col = function(col) { + if (!this.gridmap[col]) { + return false; + } + + for (var i = this.gridmap[col].length - 1; i >= 0; i--) { + if (this.is_widget(col, i) !== false) { + return true; + } + } + + return false; + }; + + fn.widgets_in_row = function(row) { + for (var i = this.gridmap.length; i >= 1; i--) { + if (this.is_widget(i, row) !== false) { + return true; + } + } + + return false; + }; + + + fn.widgets_in_range = function(col1, row1, col2, row2) { + var valid_cols = []; + var valid_rows = []; + var $widgets = $([]); + var c, r, $w, wgd; + + for (c = col2; c >= col1; c--) { + for (r = row2; r >= row1; r--) { + $w = this.is_widget(c, r); + + if ($w !== false) { + wgd = $w.data('coords').grid; + if (wgd.col >= col1 && wgd.col <= col2 && + wgd.row >= row1 && wgd.row <= row2 + ) { + $widgets = $widgets.add($w); + } + } + } + } + + return $widgets; + }; + + + fn.get_bottom_most_occupied_cell = function() { + var row = 0; + var col = 0; + this.for_each_cell(function($el, c, r) { + if ($el && r > row) { + row = r; + col = c; + } + }); + + return {col: col, row: row}; + }; + + + fn.get_right_most_occupied_cell = function() { + var row = 0; + var col = 0; + this.for_each_cell(function($el, c, r) { + if ($el) { + row = r; + col = c; + return false; + } + }); + + return {col: col, row: row}; + }; + + + fn.for_each_cell = function(callback, gridmap) { + gridmap || (gridmap = this.gridmap); + var cols = gridmap.length; + var rows = gridmap[1].length; + + cols_iter: + for (var c = cols - 1; c >= 1; c--) { + for (var r = rows - 1; r >= 1; r--) { + var $el = gridmap[c] && gridmap[c][r]; + if (callback) { + if (callback.call(this, $el, c, r) === false) { + break cols_iter; + } else { continue; } + } + } + } + }; + + + fn.next_position_in_range = function(size_x, size_y, max_rows) { + size_x || (size_x = 1); + size_y || (size_y = 1); + var ga = this.gridmap; + var cols_l = ga.length; + var valid_pos = []; + var rows_l; + + for (var c = 1; c < cols_l; c++) { + rows_l = max_rows || ga[c].length; + for (var r = 1; r <= rows_l; r++) { + var can_move_to = this.can_move_to({ + size_x: size_x, + size_y: size_y + }, c, r, max_rows); + + if (can_move_to) { + valid_pos.push({ + col: c, + row: r, + size_y: size_y, + size_x: size_x + }); + } + } + } + + if (valid_pos.length >= 1) { + return Gridster.sort_by_col_asc(valid_pos)[0]; + } + + return false; + }; + + + fn.closest_to_right = function(col, row) { + if (!this.gridmap[col]) { return false; } + var cols_l = this.gridmap.length - 1; + + for (var c = col; c <= cols_l; c++) { + if (this.gridmap[c][row]) { + return { col: c, row: row }; + } + } + + return false; + }; + + + fn.closest_to_left = function(col, row) { + var cols_l = this.gridmap.length - 1; + if (!this.gridmap[col]) { return false; } + + for (var c = col; c >= 1; c--) { + if (this.gridmap[c][row]) { + return { col: c, row: row }; + } + } + + return false; + }; + + return Gridster; + +})); diff --git a/GRID/src/jquery.gridster.js b/GRID/src/jquery.gridster.js new file mode 100644 index 0000000..7bac911 --- /dev/null +++ b/GRID/src/jquery.gridster.js @@ -0,0 +1,3140 @@ +/* + * jquery.gridster + * https://github.com/ducksboard/gridster.js + * + * Copyright (c) 2012 ducksboard + * Licensed under the MIT licenses. + */ + +;(function(root, factory) { + + if (typeof define === 'function' && define.amd) { + define(['jquery', 'gridster-draggable', 'gridster-collision'], factory); + } else { + root.Gridster = factory(root.$ || root.jQuery, root.GridsterDraggable, + root.GridsterCollision); + } + + }(this, function($, Draggable, Collision) { + + var defaults = { + namespace: '', + widget_selector: 'li', + widget_margins: [10, 10], + widget_base_dimensions: [400, 225], + extra_rows: 0, + extra_cols: 0, + min_cols: 1, + max_cols: Infinity, + min_rows: 15, + max_size_x: false, + autogrow_cols: false, + autogenerate_stylesheet: true, + avoid_overlapped_widgets: true, + auto_init: true, + serialize_params: function($w, wgd) { + return { + col: wgd.col, + row: wgd.row, + size_x: wgd.size_x, + size_y: wgd.size_y + }; + }, + collision: {}, + draggable: { + items: '.gs-w', + distance: 4, + ignore_dragging: Draggable.defaults.ignore_dragging.slice(0) + }, + resize: { + enabled: false, + axes: ['both'], + handle_append_to: '', + handle_class: 'gs-resize-handle', + max_size: [Infinity, Infinity], + min_size: [1, 1] + } + }; + + /** + * @class Gridster + * @uses Draggable + * @uses Collision + * @param {HTMLElement} el The HTMLelement that contains all the widgets. + * @param {Object} [options] An Object with all options you want to + * overwrite: + * @param {HTMLElement|String} [options.widget_selector] Define who will + * be the draggable widgets. Can be a CSS Selector String or a + * collection of HTMLElements + * @param {Array} [options.widget_margins] Margin between widgets. + * The first index for the horizontal margin (left, right) and + * the second for the vertical margin (top, bottom). + * @param {Array} [options.widget_base_dimensions] Base widget dimensions + * in pixels. The first index for the width and the second for the + * height. + * @param {Number} [options.extra_cols] Add more columns in addition to + * those that have been calculated. + * @param {Number} [options.extra_rows] Add more rows in addition to + * those that have been calculated. + * @param {Number} [options.min_cols] The minimum required columns. + * @param {Number} [options.max_cols] The maximum columns possible (set to null + * for no maximum). + * @param {Number} [options.min_rows] The minimum required rows. + * @param {Number} [options.max_size_x] The maximum number of columns + * that a widget can span. + * @param {Boolean} [options.autogenerate_stylesheet] If true, all the + * CSS required to position all widgets in their respective columns + * and rows will be generated automatically and injected to the + * `` of the document. You can set this to false, and write + * your own CSS targeting rows and cols via data-attributes like so: + * `[data-col="1"] { left: 10px; }` + * @param {Boolean} [options.avoid_overlapped_widgets] Avoid that widgets loaded + * from the DOM can be overlapped. It is helpful if the positions were + * bad stored in the database or if there was any conflict. + * @param {Boolean} [options.auto_init] Automatically call gridster init + * method or not when the plugin is instantiated. + * @param {Function} [options.serialize_params] Return the data you want + * for each widget in the serialization. Two arguments are passed: + * `$w`: the jQuery wrapped HTMLElement, and `wgd`: the grid + * coords object (`col`, `row`, `size_x`, `size_y`). + * @param {Object} [options.collision] An Object with all options for + * Collision class you want to overwrite. See Collision docs for + * more info. + * @param {Object} [options.draggable] An Object with all options for + * Draggable class you want to overwrite. See Draggable docs for more + * info. + * @param {Object|Function} [options.draggable.ignore_dragging] Note that + * if you use a Function, and resize is enabled, you should ignore the + * resize handlers manually (options.resize.handle_class). + * @param {Object} [options.resize] An Object with resize config options. + * @param {Boolean} [options.resize.enabled] Set to true to enable + * resizing. + * @param {Array} [options.resize.axes] Axes in which widgets can be + * resized. Possible values: ['x', 'y', 'both']. + * @param {String} [options.resize.handle_append_to] Set a valid CSS + * selector to append resize handles to. + * @param {String} [options.resize.handle_class] CSS class name used + * by resize handles. + * @param {Array} [options.resize.max_size] Limit widget dimensions + * when resizing. Array values should be integers: + * `[max_cols_occupied, max_rows_occupied]` + * @param {Array} [options.resize.min_size] Limit widget dimensions + * when resizing. Array values should be integers: + * `[min_cols_occupied, min_rows_occupied]` + * @param {Function} [options.resize.start] Function executed + * when resizing starts. + * @param {Function} [otions.resize.resize] Function executed + * during the resizing. + * @param {Function} [options.resize.stop] Function executed + * when resizing stops. + * + * @constructor + */ + function Gridster(el, options) { + this.options = $.extend(true, {}, defaults, options); + this.$el = $(el); + this.$wrapper = this.$el.parent(); + this.$widgets = this.$el.children( + this.options.widget_selector).addClass('gs-w'); + this.widgets = []; + this.$changed = $([]); + this.wrapper_width = this.$wrapper.width(); + this.min_widget_width = (this.options.widget_margins[0] * 2) + + this.options.widget_base_dimensions[0]; + this.min_widget_height = (this.options.widget_margins[1] * 2) + + this.options.widget_base_dimensions[1]; + + this.generated_stylesheets = []; + this.$style_tags = $([]); + + this.options.auto_init && this.init(); + } + + Gridster.defaults = defaults; + Gridster.generated_stylesheets = []; + + + /** + * Sorts an Array of grid coords objects (representing the grid coords of + * each widget) in ascending way. + * + * @method sort_by_row_asc + * @param {Array} widgets Array of grid coords objects + * @return {Array} Returns the array sorted. + */ + Gridster.sort_by_row_asc = function(widgets) { + widgets = widgets.sort(function(a, b) { + if (!a.row) { + a = $(a).coords().grid; + b = $(b).coords().grid; + } + + if (a.row > b.row) { + return 1; + } + return -1; + }); + + return widgets; + }; + + + /** + * Sorts an Array of grid coords objects (representing the grid coords of + * each widget) placing first the empty cells upper left. + * + * @method sort_by_row_and_col_asc + * @param {Array} widgets Array of grid coords objects + * @return {Array} Returns the array sorted. + */ + Gridster.sort_by_row_and_col_asc = function(widgets) { + widgets = widgets.sort(function(a, b) { + if (a.row > b.row || a.row === b.row && a.col > b.col) { + return 1; + } + return -1; + }); + + return widgets; + }; + + + /** + * Sorts an Array of grid coords objects by column (representing the grid + * coords of each widget) in ascending way. + * + * @method sort_by_col_asc + * @param {Array} widgets Array of grid coords objects + * @return {Array} Returns the array sorted. + */ + Gridster.sort_by_col_asc = function(widgets) { + widgets = widgets.sort(function(a, b) { + if (a.col > b.col) { + return 1; + } + return -1; + }); + + return widgets; + }; + + + /** + * Sorts an Array of grid coords objects (representing the grid coords of + * each widget) in descending way. + * + * @method sort_by_row_desc + * @param {Array} widgets Array of grid coords objects + * @return {Array} Returns the array sorted. + */ + Gridster.sort_by_row_desc = function(widgets) { + widgets = widgets.sort(function(a, b) { + if (a.row + a.size_y < b.row + b.size_y) { + return 1; + } + return -1; + }); + return widgets; + }; + + + + /** Instance Methods **/ + + var fn = Gridster.prototype; + + fn.init = function() { + this.options.resize.enabled && this.setup_resize(); + this.generate_grid_and_stylesheet(); + this.get_widgets_from_DOM(); + this.set_dom_grid_height(); + this.set_dom_grid_width(); + this.$wrapper.addClass('ready'); + this.draggable(); + this.options.resize.enabled && this.resizable(); + + $(window).bind('resize.gridster', throttle( + $.proxy(this.recalculate_faux_grid, this), 200)); + }; + + + /** + * Disables dragging. + * + * @method disable + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.disable = function() { + this.$wrapper.find('.player-revert').removeClass('player-revert'); + this.drag_api.disable(); + return this; + }; + + + /** + * Enables dragging. + * + * @method enable + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.enable = function() { + this.drag_api.enable(); + return this; + }; + + + + /** + * Disables drag-and-drop widget resizing. + * + * @method disable + * @return {Class} Returns instance of gridster Class. + */ + fn.disable_resize = function() { + this.$el.addClass('gs-resize-disabled'); + this.resize_api.disable(); + return this; + }; + + + /** + * Enables drag-and-drop widget resizing. + * + * @method enable + * @return {Class} Returns instance of gridster Class. + */ + fn.enable_resize = function() { + this.$el.removeClass('gs-resize-disabled'); + this.resize_api.enable(); + return this; + }; + + + /** + * Add a new widget to the grid. + * + * @method add_widget + * @param {String|HTMLElement} html The string representing the HTML of the widget + * or the HTMLElement. + * @param {Number} [size_x] The nº of rows the widget occupies horizontally. + * @param {Number} [size_y] The nº of columns the widget occupies vertically. + * @param {Number} [col] The column the widget should start in. + * @param {Number} [row] The row the widget should start in. + * @param {Array} [max_size] max_size Maximun size (in units) for width and height. + * @param {Array} [min_size] min_size Minimum size (in units) for width and height. + * @return {HTMLElement} Returns the jQuery wrapped HTMLElement representing. + * the widget that was just created. + */ + fn.add_widget = function(html, size_x, size_y, col, row, max_size, min_size) { + var pos; + size_x || (size_x = 1); + size_y || (size_y = 1); + + if (!col & !row) { + pos = this.next_position(size_x, size_y); + } else { + pos = { + col: col, + row: row, + size_x: size_x, + size_y: size_y + }; + + this.empty_cells(col, row, size_x, size_y); + } + + var $w = $(html).attr({ + 'data-col': pos.col, + 'data-row': pos.row, + 'data-sizex' : size_x, + 'data-sizey' : size_y + }).addClass('gs-w').appendTo(this.$el).hide(); + + this.$widgets = this.$widgets.add($w); + + this.register_widget($w); + + this.add_faux_rows(pos.size_y); + //this.add_faux_cols(pos.size_x); + + if (max_size) { + this.set_widget_max_size($w, max_size); + } + + if (min_size) { + this.set_widget_min_size($w, min_size); + } + + this.set_dom_grid_width(); + this.set_dom_grid_height(); + + this.drag_api.set_limits(this.cols * this.min_widget_width); + + return $w.fadeIn(); + }; + + + /** + * Change widget size limits. + * + * @method set_widget_min_size + * @param {HTMLElement|Number} $widget The jQuery wrapped HTMLElement + * representing the widget or an index representing the desired widget. + * @param {Array} min_size Minimum size (in units) for width and height. + * @return {HTMLElement} Returns instance of gridster Class. + */ + fn.set_widget_min_size = function($widget, min_size) { + $widget = typeof $widget === 'number' ? + this.$widgets.eq($widget) : $widget; + + if (!$widget.length) { return this; } + + var wgd = $widget.data('coords').grid; + wgd.min_size_x = min_size[0]; + wgd.min_size_y = min_size[1]; + + return this; + }; + + + /** + * Change widget size limits. + * + * @method set_widget_max_size + * @param {HTMLElement|Number} $widget The jQuery wrapped HTMLElement + * representing the widget or an index representing the desired widget. + * @param {Array} max_size Maximun size (in units) for width and height. + * @return {HTMLElement} Returns instance of gridster Class. + */ + fn.set_widget_max_size = function($widget, max_size) { + $widget = typeof $widget === 'number' ? + this.$widgets.eq($widget) : $widget; + + if (!$widget.length) { return this; } + + var wgd = $widget.data('coords').grid; + wgd.max_size_x = max_size[0]; + wgd.max_size_y = max_size[1]; + + return this; + }; + + + /** + * Append the resize handle into a widget. + * + * @method add_resize_handle + * @param {HTMLElement} $widget The jQuery wrapped HTMLElement + * representing the widget. + * @return {HTMLElement} Returns instance of gridster Class. + */ + fn.add_resize_handle = function($w) { + var append_to = this.options.resize.handle_append_to; + $(this.resize_handle_tpl).appendTo( append_to ? $(append_to, $w) : $w); + + return this; + }; + + + /** + * Change the size of a widget. Width is limited to the current grid width. + * + * @method resize_widget + * @param {HTMLElement} $widget The jQuery wrapped HTMLElement + * representing the widget. + * @param {Number} size_x The number of columns that will occupy the widget. + * By default size_x is limited to the space available from + * the column where the widget begins, until the last column to the right. + * @param {Number} size_y The number of rows that will occupy the widget. + * @param {Function} [callback] Function executed when the widget is removed. + * @return {HTMLElement} Returns $widget. + */ + fn.resize_widget = function($widget, size_x, size_y, callback) { + var wgd = $widget.coords().grid; + var col = wgd.col; + var max_cols = this.options.max_cols; + var old_size_y = wgd.size_y; + var old_col = wgd.col; + var new_col = old_col; + + size_x || (size_x = wgd.size_x); + size_y || (size_y = wgd.size_y); + + if (max_cols !== Infinity) { + size_x = Math.min(size_x, max_cols - col + 1); + } + + if (size_y > old_size_y) { + this.add_faux_rows(Math.max(size_y - old_size_y, 0)); + } + + var player_rcol = (col + size_x - 1); + if (player_rcol > this.cols) { + this.add_faux_cols(player_rcol - this.cols); + } + + var new_grid_data = { + col: new_col, + row: wgd.row, + size_x: size_x, + size_y: size_y + }; + + this.mutate_widget_in_gridmap($widget, wgd, new_grid_data); + + this.set_dom_grid_height(); + this.set_dom_grid_width(); + + if (callback) { + callback.call(this, new_grid_data.size_x, new_grid_data.size_y); + } + + return $widget; + }; + + + /** + * Mutate widget dimensions and position in the grid map. + * + * @method mutate_widget_in_gridmap + * @param {HTMLElement} $widget The jQuery wrapped HTMLElement + * representing the widget to mutate. + * @param {Object} wgd Current widget grid data (col, row, size_x, size_y). + * @param {Object} new_wgd New widget grid data. + * @return {HTMLElement} Returns instance of gridster Class. + */ + fn.mutate_widget_in_gridmap = function($widget, wgd, new_wgd) { + var old_size_x = wgd.size_x; + var old_size_y = wgd.size_y; + + var old_cells_occupied = this.get_cells_occupied(wgd); + var new_cells_occupied = this.get_cells_occupied(new_wgd); + + var empty_cols = []; + $.each(old_cells_occupied.cols, function(i, col) { + if ($.inArray(col, new_cells_occupied.cols) === -1) { + empty_cols.push(col); + } + }); + + var occupied_cols = []; + $.each(new_cells_occupied.cols, function(i, col) { + if ($.inArray(col, old_cells_occupied.cols) === -1) { + occupied_cols.push(col); + } + }); + + var empty_rows = []; + $.each(old_cells_occupied.rows, function(i, row) { + if ($.inArray(row, new_cells_occupied.rows) === -1) { + empty_rows.push(row); + } + }); + + var occupied_rows = []; + $.each(new_cells_occupied.rows, function(i, row) { + if ($.inArray(row, old_cells_occupied.rows) === -1) { + occupied_rows.push(row); + } + }); + + this.remove_from_gridmap(wgd); + + if (occupied_cols.length) { + var cols_to_empty = [ + new_wgd.col, new_wgd.row, new_wgd.size_x, Math.min(old_size_y, new_wgd.size_y), $widget + ]; + this.empty_cells.apply(this, cols_to_empty); + } + + if (occupied_rows.length) { + var rows_to_empty = [new_wgd.col, new_wgd.row, new_wgd.size_x, new_wgd.size_y, $widget]; + this.empty_cells.apply(this, rows_to_empty); + } + + // not the same that wgd = new_wgd; + wgd.col = new_wgd.col; + wgd.row = new_wgd.row; + wgd.size_x = new_wgd.size_x; + wgd.size_y = new_wgd.size_y; + + this.add_to_gridmap(new_wgd, $widget); + + $widget.removeClass('player-revert'); + + //update coords instance attributes + $widget.data('coords').update({ + width: (new_wgd.size_x * this.options.widget_base_dimensions[0] + + ((new_wgd.size_x - 1) * this.options.widget_margins[0]) * 2), + height: (new_wgd.size_y * this.options.widget_base_dimensions[1] + + ((new_wgd.size_y - 1) * this.options.widget_margins[1]) * 2) + }); + + $widget.attr({ + 'data-col': new_wgd.col, + 'data-row': new_wgd.row, + 'data-sizex': new_wgd.size_x, + 'data-sizey': new_wgd.size_y + }); + + if (empty_cols.length) { + var cols_to_remove_holes = [ + empty_cols[0], new_wgd.row, + empty_cols.length, + Math.min(old_size_y, new_wgd.size_y), + $widget + ]; + + this.remove_empty_cells.apply(this, cols_to_remove_holes); + } + + if (empty_rows.length) { + var rows_to_remove_holes = [ + new_wgd.col, new_wgd.row, new_wgd.size_x, new_wgd.size_y, $widget + ]; + this.remove_empty_cells.apply(this, rows_to_remove_holes); + } + + this.move_widget_up($widget); + + return this; + }; + + + /** + * Move down widgets in cells represented by the arguments col, row, size_x, + * size_y + * + * @method empty_cells + * @param {Number} col The column where the group of cells begin. + * @param {Number} row The row where the group of cells begin. + * @param {Number} size_x The number of columns that the group of cells + * occupy. + * @param {Number} size_y The number of rows that the group of cells + * occupy. + * @param {HTMLElement} $exclude Exclude widgets from being moved. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.empty_cells = function(col, row, size_x, size_y, $exclude) { + var $nexts = this.widgets_below({ + col: col, + row: row - size_y, + size_x: size_x, + size_y: size_y + }); + + $nexts.not($exclude).each($.proxy(function(i, w) { + var wgd = $(w).coords().grid; + if ( !(wgd.row <= (row + size_y - 1))) { return; } + var diff = (row + size_y) - wgd.row; + this.move_widget_down($(w), diff); + }, this)); + + this.set_dom_grid_height(); + + return this; + }; + + + /** + * Move up widgets below cells represented by the arguments col, row, size_x, + * size_y. + * + * @method remove_empty_cells + * @param {Number} col The column where the group of cells begin. + * @param {Number} row The row where the group of cells begin. + * @param {Number} size_x The number of columns that the group of cells + * occupy. + * @param {Number} size_y The number of rows that the group of cells + * occupy. + * @param {HTMLElement} exclude Exclude widgets from being moved. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.remove_empty_cells = function(col, row, size_x, size_y, exclude) { + var $nexts = this.widgets_below({ + col: col, + row: row, + size_x: size_x, + size_y: size_y + }); + + $nexts.not(exclude).each($.proxy(function(i, widget) { + this.move_widget_up( $(widget), size_y ); + }, this)); + + this.set_dom_grid_height(); + + return this; + }; + + + /** + * Get the most left column below to add a new widget. + * + * @method next_position + * @param {Number} size_x The nº of rows the widget occupies horizontally. + * @param {Number} size_y The nº of columns the widget occupies vertically. + * @return {Object} Returns a grid coords object representing the future + * widget coords. + */ + fn.next_position = function(size_x, size_y) { + size_x || (size_x = 1); + size_y || (size_y = 1); + var ga = this.gridmap; + var cols_l = ga.length; + var valid_pos = []; + var rows_l; + + for (var c = 1; c < cols_l; c++) { + rows_l = ga[c].length; + for (var r = 1; r <= rows_l; r++) { + var can_move_to = this.can_move_to({ + size_x: size_x, + size_y: size_y + }, c, r); + + if (can_move_to) { + valid_pos.push({ + col: c, + row: r, + size_y: size_y, + size_x: size_x + }); + } + } + } + + if (valid_pos.length) { + return Gridster.sort_by_row_and_col_asc(valid_pos)[0]; + } + return false; + }; + + + /** + * Remove a widget from the grid. + * + * @method remove_widget + * @param {HTMLElement} el The jQuery wrapped HTMLElement you want to remove. + * @param {Boolean|Function} silent If true, widgets below the removed one + * will not move up. If a Function is passed it will be used as callback. + * @param {Function} callback Function executed when the widget is removed. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.remove_widget = function(el, silent, callback) { + var $el = el instanceof $ ? el : $(el); + var wgd = $el.coords().grid; + + // if silent is a function assume it's a callback + if ($.isFunction(silent)) { + callback = silent; + silent = false; + } + + this.cells_occupied_by_placeholder = {}; + this.$widgets = this.$widgets.not($el); + + var $nexts = this.widgets_below($el); + + this.remove_from_gridmap(wgd); + + $el.fadeOut($.proxy(function() { + $el.remove(); + + if (!silent) { + $nexts.each($.proxy(function(i, widget) { + this.move_widget_up( $(widget), wgd.size_y ); + }, this)); + } + + this.set_dom_grid_height(); + + if (callback) { + callback.call(this, el); + } + }, this)); + + return this; + }; + + + /** + * Remove all widgets from the grid. + * + * @method remove_all_widgets + * @param {Function} callback Function executed for each widget removed. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.remove_all_widgets = function(callback) { + this.$widgets.each($.proxy(function(i, el){ + this.remove_widget(el, true, callback); + }, this)); + + return this; + }; + + + /** + * Returns a serialized array of the widgets in the grid. + * + * @method serialize + * @param {HTMLElement} [$widgets] The collection of jQuery wrapped + * HTMLElements you want to serialize. If no argument is passed all widgets + * will be serialized. + * @return {Array} Returns an Array of Objects with the data specified in + * the serialize_params option. + */ + fn.serialize = function($widgets) { + $widgets || ($widgets = this.$widgets); + + return $widgets.map($.proxy(function(i, widget) { + var $w = $(widget); + return this.options.serialize_params($w, $w.coords().grid); + }, this)).get(); + }; + + + /** + * Returns a serialized array of the widgets that have changed their + * position. + * + * @method serialize_changed + * @return {Array} Returns an Array of Objects with the data specified in + * the serialize_params option. + */ + fn.serialize_changed = function() { + return this.serialize(this.$changed); + }; + + + /** + * Convert widgets from DOM elements to "widget grid data" Objects. + * + * @method dom_to_coords + * @param {HTMLElement} $widget The widget to be converted. + */ + fn.dom_to_coords = function($widget) { + return { + 'col': parseInt($widget.attr('data-col'), 10), + 'row': parseInt($widget.attr('data-row'), 10), + 'size_x': parseInt($widget.attr('data-sizex'), 10) || 1, + 'size_y': parseInt($widget.attr('data-sizey'), 10) || 1, + 'max_size_x': parseInt($widget.attr('data-max-sizex'), 10) || false, + 'max_size_y': parseInt($widget.attr('data-max-sizey'), 10) || false, + 'min_size_x': parseInt($widget.attr('data-min-sizex'), 10) || false, + 'min_size_y': parseInt($widget.attr('data-min-sizey'), 10) || false, + 'el': $widget + }; + }; + + + /** + * Creates the grid coords object representing the widget an add it to the + * mapped array of positions. + * + * @method register_widget + * @param {HTMLElement|Object} $el jQuery wrapped HTMLElement representing + * the widget, or an "widget grid data" Object with (col, row, el ...). + * @return {Boolean} Returns true if the widget final position is different + * than the original. + */ + fn.register_widget = function($el) { + var isDOM = $el instanceof jQuery; + var wgd = isDOM ? this.dom_to_coords($el) : $el; + var posChanged = false; + isDOM || ($el = wgd.el); + + var empty_upper_row = this.can_go_widget_up(wgd); + if (empty_upper_row) { + wgd.row = empty_upper_row; + $el.attr('data-row', empty_upper_row); + this.$el.trigger('gridster:positionchanged', [wgd]); + posChanged = true; + } + + if (this.options.avoid_overlapped_widgets && + !this.can_move_to( + {size_x: wgd.size_x, size_y: wgd.size_y}, wgd.col, wgd.row) + ) { + $.extend(wgd, this.next_position(wgd.size_x, wgd.size_y)); + $el.attr({ + 'data-col': wgd.col, + 'data-row': wgd.row, + 'data-sizex': wgd.size_x, + 'data-sizey': wgd.size_y + }); + posChanged = true; + } + + // attach Coord object to player data-coord attribute + $el.data('coords', $el.coords()); + // Extend Coord object with grid position info + $el.data('coords').grid = wgd; + + this.add_to_gridmap(wgd, $el); + + this.options.resize.enabled && this.add_resize_handle($el); + + return posChanged; + }; + + + /** + * Update in the mapped array of positions the value of cells represented by + * the grid coords object passed in the `grid_data` param. + * + * @param {Object} grid_data The grid coords object representing the cells + * to update in the mapped array. + * @param {HTMLElement|Boolean} value Pass `false` or the jQuery wrapped + * HTMLElement, depends if you want to delete an existing position or add + * a new one. + * @method update_widget_position + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.update_widget_position = function(grid_data, value) { + this.for_each_cell_occupied(grid_data, function(col, row) { + if (!this.gridmap[col]) { return this; } + this.gridmap[col][row] = value; + }); + return this; + }; + + + /** + * Remove a widget from the mapped array of positions. + * + * @method remove_from_gridmap + * @param {Object} grid_data The grid coords object representing the cells + * to update in the mapped array. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.remove_from_gridmap = function(grid_data) { + return this.update_widget_position(grid_data, false); + }; + + + /** + * Add a widget to the mapped array of positions. + * + * @method add_to_gridmap + * @param {Object} grid_data The grid coords object representing the cells + * to update in the mapped array. + * @param {HTMLElement|Boolean} value The value to set in the specified + * position . + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.add_to_gridmap = function(grid_data, value) { + this.update_widget_position(grid_data, value || grid_data.el); + + if (grid_data.el) { + var $widgets = this.widgets_below(grid_data.el); + $widgets.each($.proxy(function(i, widget) { + this.move_widget_up( $(widget)); + }, this)); + } + }; + + + /** + * Make widgets draggable. + * + * @uses Draggable + * @method draggable + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.draggable = function() { + var self = this; + var draggable_options = $.extend(true, {}, this.options.draggable, { + offset_left: this.options.widget_margins[0], + offset_top: this.options.widget_margins[1], + container_width: this.cols * this.min_widget_width, + limit: true, + start: function(event, ui) { + self.$widgets.filter('.player-revert') + .removeClass('player-revert'); + + self.$player = $(this); + self.$helper = $(ui.$helper); + + self.helper = !self.$helper.is(self.$player); + + self.on_start_drag.call(self, event, ui); + self.$el.trigger('gridster:dragstart'); + }, + stop: function(event, ui) { + self.on_stop_drag.call(self, event, ui); + self.$el.trigger('gridster:dragstop'); + }, + drag: throttle(function(event, ui) { + self.on_drag.call(self, event, ui); + self.$el.trigger('gridster:drag'); + }, 60) + }); + + this.drag_api = this.$el.drag(draggable_options); + return this; + }; + + + /** + * Bind resize events to get resize working. + * + * @method resizable + * @return {Class} Returns instance of gridster Class. + */ + fn.resizable = function() { + this.resize_api = this.$el.drag({ + items: '.' + this.options.resize.handle_class, + offset_left: this.options.widget_margins[0], + container_width: this.container_width, + move_element: false, + resize: true, + limit: this.options.autogrow_cols ? false : true, + start: $.proxy(this.on_start_resize, this), + stop: $.proxy(function(event, ui) { + delay($.proxy(function() { + this.on_stop_resize(event, ui); + }, this), 120); + }, this), + drag: throttle($.proxy(this.on_resize, this), 60) + }); + + return this; + }; + + + /** + * Setup things required for resizing. Like build templates for drag handles. + * + * @method setup_resize + * @return {Class} Returns instance of gridster Class. + */ + fn.setup_resize = function() { + this.resize_handle_class = this.options.resize.handle_class; + var axes = this.options.resize.axes; + var handle_tpl = ''; + + this.resize_handle_tpl = $.map(axes, function(type) { + return handle_tpl.replace('{type}', type); + }).join(''); + + if ($.isArray(this.options.draggable.ignore_dragging)) { + this.options.draggable.ignore_dragging.push( + '.' + this.resize_handle_class); + } + + return this; + }; + + + /** + * This function is executed when the player begins to be dragged. + * + * @method on_start_drag + * @param {Event} event The original browser event + * @param {Object} ui A prepared ui object with useful drag-related data + */ + fn.on_start_drag = function(event, ui) { + this.$helper.add(this.$player).add(this.$wrapper).addClass('dragging'); + + this.highest_col = this.get_highest_occupied_cell().col; + + this.$player.addClass('player'); + this.player_grid_data = this.$player.coords().grid; + this.placeholder_grid_data = $.extend({}, this.player_grid_data); + + this.set_dom_grid_height(this.$el.height() + + (this.player_grid_data.size_y * this.min_widget_height)); + + this.set_dom_grid_width(this.cols); + + var pgd_sizex = this.player_grid_data.size_x; + var cols_diff = this.cols - this.highest_col; + + if (this.options.autogrow_cols && cols_diff <= pgd_sizex) { + this.add_faux_cols(Math.min(pgd_sizex - cols_diff, 1)); + } + + var colliders = this.faux_grid; + var coords = this.$player.data('coords').coords; + + this.cells_occupied_by_player = this.get_cells_occupied( + this.player_grid_data); + this.cells_occupied_by_placeholder = this.get_cells_occupied( + this.placeholder_grid_data); + + this.last_cols = []; + this.last_rows = []; + + // see jquery.collision.js + this.collision_api = this.$helper.collision( + colliders, this.options.collision); + + this.$preview_holder = $('<' + this.$player.get(0).tagName + ' />', { + 'class': 'preview-holder', + 'data-row': this.$player.attr('data-row'), + 'data-col': this.$player.attr('data-col'), + css: { + width: coords.width, + height: coords.height + } + }).appendTo(this.$el); + + if (this.options.draggable.start) { + this.options.draggable.start.call(this, event, ui); + } + }; + + + /** + * This function is executed when the player is being dragged. + * + * @method on_drag + * @param {Event} event The original browser event + * @param {Object} ui A prepared ui object with useful drag-related data + */ + fn.on_drag = function(event, ui) { + //break if dragstop has been fired + if (this.$player === null) { + return false; + } + + var abs_offset = { + left: ui.position.left + this.baseX, + top: ui.position.top + this.baseY + }; + + // auto grow cols + if (this.options.autogrow_cols) { + var prcol = this.placeholder_grid_data.col + + this.placeholder_grid_data.size_x - 1; + + // "- 1" due to adding at least 1 column in on_start_drag + if (prcol >= this.cols - 1 && this.options.max_cols >= this.cols + 1) { + this.add_faux_cols(1); + this.set_dom_grid_width(this.cols + 1); + this.drag_api.set_limits(this.container_width); + } + + this.collision_api.set_colliders(this.faux_grid); + } + + this.colliders_data = this.collision_api.get_closest_colliders( + abs_offset); + + this.on_overlapped_column_change( + this.on_start_overlapping_column, this.on_stop_overlapping_column); + + this.on_overlapped_row_change( + this.on_start_overlapping_row, this.on_stop_overlapping_row); + + + if (this.helper && this.$player) { + this.$player.css({ + 'left': ui.position.left, + 'top': ui.position.top + }); + } + + if (this.options.draggable.drag) { + this.options.draggable.drag.call(this, event, ui); + } + }; + + + /** + * This function is executed when the player stops being dragged. + * + * @method on_stop_drag + * @param {Event} event The original browser event + * @param {Object} ui A prepared ui object with useful drag-related data + */ + fn.on_stop_drag = function(event, ui) { + this.$helper.add(this.$player).add(this.$wrapper) + .removeClass('dragging'); + + ui.position.left = ui.position.left + this.baseX; + ui.position.top = ui.position.top + this.baseY; + this.colliders_data = this.collision_api.get_closest_colliders( + ui.position); + + this.on_overlapped_column_change( + this.on_start_overlapping_column, + this.on_stop_overlapping_column + ); + + this.on_overlapped_row_change( + this.on_start_overlapping_row, + this.on_stop_overlapping_row + ); + + this.$player.addClass('player-revert').removeClass('player') + .attr({ + 'data-col': this.placeholder_grid_data.col, + 'data-row': this.placeholder_grid_data.row + }).css({ + 'left': '', + 'top': '' + }); + + this.$changed = this.$changed.add(this.$player); + + this.cells_occupied_by_player = this.get_cells_occupied( + this.placeholder_grid_data); + this.set_cells_player_occupies( + this.placeholder_grid_data.col, this.placeholder_grid_data.row); + + this.$player.coords().grid.row = this.placeholder_grid_data.row; + this.$player.coords().grid.col = this.placeholder_grid_data.col; + + if (this.options.draggable.stop) { + this.options.draggable.stop.call(this, event, ui); + } + + this.$preview_holder.remove(); + + this.$player = null; + this.$helper = null; + this.placeholder_grid_data = {}; + this.player_grid_data = {}; + this.cells_occupied_by_placeholder = {}; + this.cells_occupied_by_player = {}; + + this.set_dom_grid_height(); + this.set_dom_grid_width(); + + if (this.options.autogrow_cols) { + this.drag_api.set_limits(this.cols * this.min_widget_width); + } + }; + + + /** + * This function is executed every time a widget starts to be resized. + * + * @method on_start_resize + * @param {Event} event The original browser event + * @param {Object} ui A prepared ui object with useful drag-related data + */ + fn.on_start_resize = function(event, ui) { + this.$resized_widget = ui.$player.closest('.gs-w'); + this.resize_coords = this.$resized_widget.coords(); + this.resize_wgd = this.resize_coords.grid; + this.resize_initial_width = this.resize_coords.coords.width; + this.resize_initial_height = this.resize_coords.coords.height; + this.resize_initial_sizex = this.resize_coords.grid.size_x; + this.resize_initial_sizey = this.resize_coords.grid.size_y; + this.resize_initial_col = this.resize_coords.grid.col; + this.resize_last_sizex = this.resize_initial_sizex; + this.resize_last_sizey = this.resize_initial_sizey; + + this.resize_max_size_x = Math.min(this.resize_wgd.max_size_x || + this.options.resize.max_size[0], + this.options.max_cols - this.resize_initial_col + 1); + this.resize_max_size_y = this.resize_wgd.max_size_y || + this.options.resize.max_size[1]; + + this.resize_min_size_x = (this.resize_wgd.min_size_x || + this.options.resize.min_size[0] || 1); + this.resize_min_size_y = (this.resize_wgd.min_size_y || + this.options.resize.min_size[1] || 1); + + this.resize_initial_last_col = this.get_highest_occupied_cell().col; + + this.set_dom_grid_width(this.cols); + + this.resize_dir = { + right: ui.$player.is('.' + this.resize_handle_class + '-x'), + bottom: ui.$player.is('.' + this.resize_handle_class + '-y') + }; + + this.$resized_widget.css({ + 'min-width': this.options.widget_base_dimensions[0], + 'min-height': this.options.widget_base_dimensions[1] + }); + + var nodeName = this.$resized_widget.get(0).tagName; + this.$resize_preview_holder = $('<' + nodeName + ' />', { + 'class': 'preview-holder resize-preview-holder', + 'data-row': this.$resized_widget.attr('data-row'), + 'data-col': this.$resized_widget.attr('data-col'), + 'css': { + 'width': this.resize_initial_width, + 'height': this.resize_initial_height + } + }).appendTo(this.$el); + + this.$resized_widget.addClass('resizing'); + + if (this.options.resize.start) { + this.options.resize.start.call(this, event, ui, this.$resized_widget); + } + + this.$el.trigger('gridster:resizestart'); + }; + + + /** + * This function is executed every time a widget stops being resized. + * + * @method on_stop_resize + * @param {Event} event The original browser event + * @param {Object} ui A prepared ui object with useful drag-related data + */ + fn.on_stop_resize = function(event, ui) { + this.$resized_widget + .removeClass('resizing') + .css({ + 'width': '', + 'height': '' + }); + + delay($.proxy(function() { + this.$resize_preview_holder + .remove() + .css({ + 'min-width': '', + 'min-height': '' + }); + + if (this.options.resize.stop) { + this.options.resize.stop.call(this, event, ui, this.$resized_widget); + } + + this.$el.trigger('gridster:resizestop'); + }, this), 300); + + this.set_dom_grid_width(); + + if (this.options.autogrow_cols) { + this.drag_api.set_limits(this.cols * this.min_widget_width); + } + }; + + + /** + * This function is executed when a widget is being resized. + * + * @method on_resize + * @param {Event} event The original browser event + * @param {Object} ui A prepared ui object with useful drag-related data + */ + fn.on_resize = function(event, ui) { + var rel_x = (ui.pointer.diff_left); + var rel_y = (ui.pointer.diff_top); + var wbd_x = this.options.widget_base_dimensions[0]; + var wbd_y = this.options.widget_base_dimensions[1]; + var margin_x = this.options.widget_margins[0]; + var margin_y = this.options.widget_margins[1]; + var max_size_x = this.resize_max_size_x; + var min_size_x = this.resize_min_size_x; + var max_size_y = this.resize_max_size_y; + var min_size_y = this.resize_min_size_y; + var autogrow = this.options.autogrow_cols; + var width; + var max_width = Infinity; + var max_height = Infinity; + + var inc_units_x = Math.ceil((rel_x / (wbd_x + margin_x * 2)) - 0.2); + var inc_units_y = Math.ceil((rel_y / (wbd_y + margin_y * 2)) - 0.2); + + var size_x = Math.max(1, this.resize_initial_sizex + inc_units_x); + var size_y = Math.max(1, this.resize_initial_sizey + inc_units_y); + + var max_cols = (this.container_width / this.min_widget_width) - + this.resize_initial_col + 1; + var limit_width = ((max_cols * this.min_widget_width) - margin_x * 2); + + size_x = Math.max(Math.min(size_x, max_size_x), min_size_x); + size_x = Math.min(max_cols, size_x); + width = (max_size_x * wbd_x) + ((size_x - 1) * margin_x * 2); + max_width = Math.min(width, limit_width); + min_width = (min_size_x * wbd_x) + ((size_x - 1) * margin_x * 2); + + size_y = Math.max(Math.min(size_y, max_size_y), min_size_y); + max_height = (max_size_y * wbd_y) + ((size_y - 1) * margin_y * 2); + min_height = (min_size_y * wbd_y) + ((size_y - 1) * margin_y * 2); + + if (this.resize_dir.right) { + size_y = this.resize_initial_sizey; + } else if (this.resize_dir.bottom) { + size_x = this.resize_initial_sizex; + } + + if (autogrow) { + var last_widget_col = this.resize_initial_col + size_x - 1; + if (autogrow && this.resize_initial_last_col <= last_widget_col) { + this.set_dom_grid_width(Math.max(last_widget_col + 1, this.cols)); + + if (this.cols < last_widget_col) { + this.add_faux_cols(last_widget_col - this.cols); + } + } + } + + var css_props = {}; + !this.resize_dir.bottom && (css_props.width = Math.max(Math.min( + this.resize_initial_width + rel_x, max_width), min_width)); + !this.resize_dir.right && (css_props.height = Math.max(Math.min( + this.resize_initial_height + rel_y, max_height), min_height)); + + this.$resized_widget.css(css_props); + + if (size_x !== this.resize_last_sizex || + size_y !== this.resize_last_sizey) { + + this.resize_widget(this.$resized_widget, size_x, size_y); + this.set_dom_grid_width(this.cols); + + this.$resize_preview_holder.css({ + 'width': '', + 'height': '' + }).attr({ + 'data-row': this.$resized_widget.attr('data-row'), + 'data-sizex': size_x, + 'data-sizey': size_y + }); + } + + if (this.options.resize.resize) { + this.options.resize.resize.call(this, event, ui, this.$resized_widget); + } + + this.$el.trigger('gridster:resize'); + + this.resize_last_sizex = size_x; + this.resize_last_sizey = size_y; + }; + + + /** + * Executes the callbacks passed as arguments when a column begins to be + * overlapped or stops being overlapped. + * + * @param {Function} start_callback Function executed when a new column + * begins to be overlapped. The column is passed as first argument. + * @param {Function} stop_callback Function executed when a column stops + * being overlapped. The column is passed as first argument. + * @method on_overlapped_column_change + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.on_overlapped_column_change = function(start_callback, stop_callback) { + if (!this.colliders_data.length) { + return this; + } + var cols = this.get_targeted_columns( + this.colliders_data[0].el.data.col); + + var last_n_cols = this.last_cols.length; + var n_cols = cols.length; + var i; + + for (i = 0; i < n_cols; i++) { + if ($.inArray(cols[i], this.last_cols) === -1) { + (start_callback || $.noop).call(this, cols[i]); + } + } + + for (i = 0; i< last_n_cols; i++) { + if ($.inArray(this.last_cols[i], cols) === -1) { + (stop_callback || $.noop).call(this, this.last_cols[i]); + } + } + + this.last_cols = cols; + + return this; + }; + + + /** + * Executes the callbacks passed as arguments when a row starts to be + * overlapped or stops being overlapped. + * + * @param {Function} start_callback Function executed when a new row begins + * to be overlapped. The row is passed as first argument. + * @param {Function} end_callback Function executed when a row stops being + * overlapped. The row is passed as first argument. + * @method on_overlapped_row_change + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.on_overlapped_row_change = function(start_callback, end_callback) { + if (!this.colliders_data.length) { + return this; + } + var rows = this.get_targeted_rows(this.colliders_data[0].el.data.row); + var last_n_rows = this.last_rows.length; + var n_rows = rows.length; + var i; + + for (i = 0; i < n_rows; i++) { + if ($.inArray(rows[i], this.last_rows) === -1) { + (start_callback || $.noop).call(this, rows[i]); + } + } + + for (i = 0; i < last_n_rows; i++) { + if ($.inArray(this.last_rows[i], rows) === -1) { + (end_callback || $.noop).call(this, this.last_rows[i]); + } + } + + this.last_rows = rows; + }; + + + /** + * Sets the current position of the player + * + * @param {Number} col + * @param {Number} row + * @param {Boolean} no_player + * @method set_player + * @return {object} + */ + fn.set_player = function(col, row, no_player) { + var self = this; + if (!no_player) { + this.empty_cells_player_occupies(); + } + var cell = !no_player ? self.colliders_data[0].el.data : {col: col}; + var to_col = cell.col; + var to_row = row || cell.row; + + this.player_grid_data = { + col: to_col, + row: to_row, + size_y : this.player_grid_data.size_y, + size_x : this.player_grid_data.size_x + }; + + this.cells_occupied_by_player = this.get_cells_occupied( + this.player_grid_data); + + var $overlapped_widgets = this.get_widgets_overlapped( + this.player_grid_data); + + var constraints = this.widgets_constraints($overlapped_widgets); + + this.manage_movements(constraints.can_go_up, to_col, to_row); + this.manage_movements(constraints.can_not_go_up, to_col, to_row); + + /* if there is not widgets overlapping in the new player position, + * update the new placeholder position. */ + if (!$overlapped_widgets.length) { + var pp = this.can_go_player_up(this.player_grid_data); + if (pp !== false) { + to_row = pp; + } + this.set_placeholder(to_col, to_row); + } + + return { + col: to_col, + row: to_row + }; + }; + + + /** + * See which of the widgets in the $widgets param collection can go to + * a upper row and which not. + * + * @method widgets_contraints + * @param {jQuery} $widgets A jQuery wrapped collection of + * HTMLElements. + * @return {object} Returns a literal Object with two keys: `can_go_up` & + * `can_not_go_up`. Each contains a set of HTMLElements. + */ + fn.widgets_constraints = function($widgets) { + var $widgets_can_go_up = $([]); + var $widgets_can_not_go_up; + var wgd_can_go_up = []; + var wgd_can_not_go_up = []; + + $widgets.each($.proxy(function(i, w) { + var $w = $(w); + var wgd = $w.coords().grid; + if (this.can_go_widget_up(wgd)) { + $widgets_can_go_up = $widgets_can_go_up.add($w); + wgd_can_go_up.push(wgd); + } else { + wgd_can_not_go_up.push(wgd); + } + }, this)); + + $widgets_can_not_go_up = $widgets.not($widgets_can_go_up); + + return { + can_go_up: Gridster.sort_by_row_asc(wgd_can_go_up), + can_not_go_up: Gridster.sort_by_row_desc(wgd_can_not_go_up) + }; + }; + + + /** + * Sorts an Array of grid coords objects (representing the grid coords of + * each widget) in descending way. + * + * @method manage_movements + * @param {jQuery} $widgets A jQuery collection of HTMLElements + * representing the widgets you want to move. + * @param {Number} to_col The column to which we want to move the widgets. + * @param {Number} to_row The row to which we want to move the widgets. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.manage_movements = function($widgets, to_col, to_row) { + $.each($widgets, $.proxy(function(i, w) { + var wgd = w; + var $w = wgd.el; + + var can_go_widget_up = this.can_go_widget_up(wgd); + + if (can_go_widget_up) { + //target CAN go up + //so move widget up + this.move_widget_to($w, can_go_widget_up); + this.set_placeholder(to_col, can_go_widget_up + wgd.size_y); + + } else { + //target can't go up + var can_go_player_up = this.can_go_player_up( + this.player_grid_data); + + if (!can_go_player_up) { + // target can't go up + // player cant't go up + // so we need to move widget down to a position that dont + // overlaps player + var y = (to_row + this.player_grid_data.size_y) - wgd.row; + + this.move_widget_down($w, y); + this.set_placeholder(to_col, to_row); + } + } + }, this)); + + return this; + }; + + /** + * Determines if there is a widget in the row and col given. Or if the + * HTMLElement passed as first argument is the player. + * + * @method is_player + * @param {Number|HTMLElement} col_or_el A jQuery wrapped collection of + * HTMLElements. + * @param {Number} [row] The column to which we want to move the widgets. + * @return {Boolean} Returns true or false. + */ + fn.is_player = function(col_or_el, row) { + if (row && !this.gridmap[col_or_el]) { return false; } + var $w = row ? this.gridmap[col_or_el][row] : col_or_el; + return $w && ($w.is(this.$player) || $w.is(this.$helper)); + }; + + + /** + * Determines if the widget that is being dragged is currently over the row + * and col given. + * + * @method is_player_in + * @param {Number} col The column to check. + * @param {Number} row The row to check. + * @return {Boolean} Returns true or false. + */ + fn.is_player_in = function(col, row) { + var c = this.cells_occupied_by_player || {}; + return $.inArray(col, c.cols) >= 0 && $.inArray(row, c.rows) >= 0; + }; + + + /** + * Determines if the placeholder is currently over the row and col given. + * + * @method is_placeholder_in + * @param {Number} col The column to check. + * @param {Number} row The row to check. + * @return {Boolean} Returns true or false. + */ + fn.is_placeholder_in = function(col, row) { + var c = this.cells_occupied_by_placeholder || {}; + return this.is_placeholder_in_col(col) && $.inArray(row, c.rows) >= 0; + }; + + + /** + * Determines if the placeholder is currently over the column given. + * + * @method is_placeholder_in_col + * @param {Number} col The column to check. + * @return {Boolean} Returns true or false. + */ + fn.is_placeholder_in_col = function(col) { + var c = this.cells_occupied_by_placeholder || []; + return $.inArray(col, c.cols) >= 0; + }; + + + /** + * Determines if the cell represented by col and row params is empty. + * + * @method is_empty + * @param {Number} col The column to check. + * @param {Number} row The row to check. + * @return {Boolean} Returns true or false. + */ + fn.is_empty = function(col, row) { + if (typeof this.gridmap[col] !== 'undefined') { + if(typeof this.gridmap[col][row] !== 'undefined' && + this.gridmap[col][row] === false + ) { + return true; + } + return false; + } + return true; + }; + + + /** + * Determines if the cell represented by col and row params is occupied. + * + * @method is_occupied + * @param {Number} col The column to check. + * @param {Number} row The row to check. + * @return {Boolean} Returns true or false. + */ + fn.is_occupied = function(col, row) { + if (!this.gridmap[col]) { + return false; + } + + if (this.gridmap[col][row]) { + return true; + } + return false; + }; + + + /** + * Determines if there is a widget in the cell represented by col/row params. + * + * @method is_widget + * @param {Number} col The column to check. + * @param {Number} row The row to check. + * @return {Boolean|HTMLElement} Returns false if there is no widget, + * else returns the jQuery HTMLElement + */ + fn.is_widget = function(col, row) { + var cell = this.gridmap[col]; + if (!cell) { + return false; + } + + cell = cell[row]; + + if (cell) { + return cell; + } + + return false; + }; + + + /** + * Determines if there is a widget in the cell represented by col/row + * params and if this is under the widget that is being dragged. + * + * @method is_widget_under_player + * @param {Number} col The column to check. + * @param {Number} row The row to check. + * @return {Boolean} Returns true or false. + */ + fn.is_widget_under_player = function(col, row) { + if (this.is_widget(col, row)) { + return this.is_player_in(col, row); + } + return false; + }; + + + /** + * Get widgets overlapping with the player or with the object passed + * representing the grid cells. + * + * @method get_widgets_under_player + * @return {HTMLElement} Returns a jQuery collection of HTMLElements + */ + fn.get_widgets_under_player = function(cells) { + cells || (cells = this.cells_occupied_by_player || {cols: [], rows: []}); + var $widgets = $([]); + + $.each(cells.cols, $.proxy(function(i, col) { + $.each(cells.rows, $.proxy(function(i, row) { + if(this.is_widget(col, row)) { + $widgets = $widgets.add(this.gridmap[col][row]); + } + }, this)); + }, this)); + + return $widgets; + }; + + + /** + * Put placeholder at the row and column specified. + * + * @method set_placeholder + * @param {Number} col The column to which we want to move the + * placeholder. + * @param {Number} row The row to which we want to move the + * placeholder. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.set_placeholder = function(col, row) { + var phgd = $.extend({}, this.placeholder_grid_data); + var $nexts = this.widgets_below({ + col: phgd.col, + row: phgd.row, + size_y: phgd.size_y, + size_x: phgd.size_x + }); + + // Prevents widgets go out of the grid + var right_col = (col + phgd.size_x - 1); + if (right_col > this.cols) { + col = col - (right_col - col); + } + + var moved_down = this.placeholder_grid_data.row < row; + var changed_column = this.placeholder_grid_data.col !== col; + + this.placeholder_grid_data.col = col; + this.placeholder_grid_data.row = row; + + this.cells_occupied_by_placeholder = this.get_cells_occupied( + this.placeholder_grid_data); + + this.$preview_holder.attr({ + 'data-row' : row, + 'data-col' : col + }); + + if (moved_down || changed_column) { + $nexts.each($.proxy(function(i, widget) { + this.move_widget_up( + $(widget), this.placeholder_grid_data.col - col + phgd.size_y); + }, this)); + } + + var $widgets_under_ph = this.get_widgets_under_player( + this.cells_occupied_by_placeholder); + + if ($widgets_under_ph.length) { + $widgets_under_ph.each($.proxy(function(i, widget) { + var $w = $(widget); + this.move_widget_down( + $w, row + phgd.size_y - $w.data('coords').grid.row); + }, this)); + } + + }; + + + /** + * Determines whether the player can move to a position above. + * + * @method can_go_player_up + * @param {Object} widget_grid_data The actual grid coords object of the + * player. + * @return {Number|Boolean} If the player can be moved to an upper row + * returns the row number, else returns false. + */ + fn.can_go_player_up = function(widget_grid_data) { + var p_bottom_row = widget_grid_data.row + widget_grid_data.size_y - 1; + var result = true; + var upper_rows = []; + var min_row = 10000; + var $widgets_under_player = this.get_widgets_under_player(); + + /* generate an array with columns as index and array with upper rows + * empty as value */ + this.for_each_column_occupied(widget_grid_data, function(tcol) { + var grid_col = this.gridmap[tcol]; + var r = p_bottom_row + 1; + upper_rows[tcol] = []; + + while (--r > 0) { + if (this.is_empty(tcol, r) || this.is_player(tcol, r) || + this.is_widget(tcol, r) && + grid_col[r].is($widgets_under_player) + ) { + upper_rows[tcol].push(r); + min_row = r < min_row ? r : min_row; + } else { + break; + } + } + + if (upper_rows[tcol].length === 0) { + result = false; + return true; //break + } + + upper_rows[tcol].sort(function(a, b) { + return a - b; + }); + }); + + if (!result) { return false; } + + return this.get_valid_rows(widget_grid_data, upper_rows, min_row); + }; + + + /** + * Determines whether a widget can move to a position above. + * + * @method can_go_widget_up + * @param {Object} widget_grid_data The actual grid coords object of the + * widget we want to check. + * @return {Number|Boolean} If the widget can be moved to an upper row + * returns the row number, else returns false. + */ + fn.can_go_widget_up = function(widget_grid_data) { + var p_bottom_row = widget_grid_data.row + widget_grid_data.size_y - 1; + var result = true; + var upper_rows = []; + var min_row = 10000; + + /* generate an array with columns as index and array with topmost rows + * empty as value */ + this.for_each_column_occupied(widget_grid_data, function(tcol) { + var grid_col = this.gridmap[tcol]; + upper_rows[tcol] = []; + + var r = p_bottom_row + 1; + // iterate over each row + while (--r > 0) { + if (this.is_widget(tcol, r) && !this.is_player_in(tcol, r)) { + if (!grid_col[r].is(widget_grid_data.el)) { + break; + } + } + + if (!this.is_player(tcol, r) && + !this.is_placeholder_in(tcol, r) && + !this.is_player_in(tcol, r)) { + upper_rows[tcol].push(r); + } + + if (r < min_row) { + min_row = r; + } + } + + if (upper_rows[tcol].length === 0) { + result = false; + return true; //break + } + + upper_rows[tcol].sort(function(a, b) { + return a - b; + }); + }); + + if (!result) { return false; } + + return this.get_valid_rows(widget_grid_data, upper_rows, min_row); + }; + + + /** + * Search a valid row for the widget represented by `widget_grid_data' in + * the `upper_rows` array. Iteration starts from row specified in `min_row`. + * + * @method get_valid_rows + * @param {Object} widget_grid_data The actual grid coords object of the + * player. + * @param {Array} upper_rows An array with columns as index and arrays + * of valid rows as values. + * @param {Number} min_row The upper row from which the iteration will start. + * @return {Number|Boolean} Returns the upper row valid from the `upper_rows` + * for the widget in question. + */ + fn.get_valid_rows = function(widget_grid_data, upper_rows, min_row) { + var p_top_row = widget_grid_data.row; + var p_bottom_row = widget_grid_data.row + widget_grid_data.size_y - 1; + var size_y = widget_grid_data.size_y; + var r = min_row - 1; + var valid_rows = []; + + while (++r <= p_bottom_row ) { + var common = true; + $.each(upper_rows, function(col, rows) { + if ($.isArray(rows) && $.inArray(r, rows) === -1) { + common = false; + } + }); + + if (common === true) { + valid_rows.push(r); + if (valid_rows.length === size_y) { + break; + } + } + } + + var new_row = false; + if (size_y === 1) { + if (valid_rows[0] !== p_top_row) { + new_row = valid_rows[0] || false; + } + } else { + if (valid_rows[0] !== p_top_row) { + new_row = this.get_consecutive_numbers_index( + valid_rows, size_y); + } + } + + return new_row; + }; + + + fn.get_consecutive_numbers_index = function(arr, size_y) { + var max = arr.length; + var result = []; + var first = true; + var prev = -1; // or null? + + for (var i=0; i < max; i++) { + if (first || arr[i] === prev + 1) { + result.push(i); + if (result.length === size_y) { + break; + } + first = false; + } else { + result = []; + first = true; + } + + prev = arr[i]; + } + + return result.length >= size_y ? arr[result[0]] : false; + }; + + + /** + * Get widgets overlapping with the player. + * + * @method get_widgets_overlapped + * @return {jQuery} Returns a jQuery collection of HTMLElements. + */ + fn.get_widgets_overlapped = function() { + var $w; + var $widgets = $([]); + var used = []; + var rows_from_bottom = this.cells_occupied_by_player.rows.slice(0); + rows_from_bottom.reverse(); + + $.each(this.cells_occupied_by_player.cols, $.proxy(function(i, col) { + $.each(rows_from_bottom, $.proxy(function(i, row) { + // if there is a widget in the player position + if (!this.gridmap[col]) { return true; } //next iteration + var $w = this.gridmap[col][row]; + if (this.is_occupied(col, row) && !this.is_player($w) && + $.inArray($w, used) === -1 + ) { + $widgets = $widgets.add($w); + used.push($w); + } + + }, this)); + }, this)); + + return $widgets; + }; + + + /** + * This callback is executed when the player begins to collide with a column. + * + * @method on_start_overlapping_column + * @param {Number} col The collided column. + * @return {jQuery} Returns a jQuery collection of HTMLElements. + */ + fn.on_start_overlapping_column = function(col) { + this.set_player(col, false); + }; + + + /** + * A callback executed when the player begins to collide with a row. + * + * @method on_start_overlapping_row + * @param {Number} row The collided row. + * @return {jQuery} Returns a jQuery collection of HTMLElements. + */ + fn.on_start_overlapping_row = function(row) { + this.set_player(false, row); + }; + + + /** + * A callback executed when the the player ends to collide with a column. + * + * @method on_stop_overlapping_column + * @param {Number} col The collided row. + * @return {jQuery} Returns a jQuery collection of HTMLElements. + */ + fn.on_stop_overlapping_column = function(col) { + this.set_player(col, false); + + var self = this; + this.for_each_widget_below(col, this.cells_occupied_by_player.rows[0], + function(tcol, trow) { + self.move_widget_up(this, self.player_grid_data.size_y); + }); + }; + + + /** + * This callback is executed when the player ends to collide with a row. + * + * @method on_stop_overlapping_row + * @param {Number} row The collided row. + * @return {jQuery} Returns a jQuery collection of HTMLElements. + */ + fn.on_stop_overlapping_row = function(row) { + this.set_player(false, row); + + var self = this; + var cols = this.cells_occupied_by_player.cols; + for (var c = 0, cl = cols.length; c < cl; c++) { + this.for_each_widget_below(cols[c], row, function(tcol, trow) { + self.move_widget_up(this, self.player_grid_data.size_y); + }); + } + }; + + + /** + * Move a widget to a specific row. The cell or cells must be empty. + * If the widget has widgets below, all of these widgets will be moved also + * if they can. + * + * @method move_widget_to + * @param {HTMLElement} $widget The jQuery wrapped HTMLElement of the + * widget is going to be moved. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.move_widget_to = function($widget, row) { + var self = this; + var widget_grid_data = $widget.coords().grid; + var diff = row - widget_grid_data.row; + var $next_widgets = this.widgets_below($widget); + + var can_move_to_new_cell = this.can_move_to( + widget_grid_data, widget_grid_data.col, row, $widget); + + if (can_move_to_new_cell === false) { + return false; + } + + this.remove_from_gridmap(widget_grid_data); + widget_grid_data.row = row; + this.add_to_gridmap(widget_grid_data); + $widget.attr('data-row', row); + this.$changed = this.$changed.add($widget); + + + $next_widgets.each(function(i, widget) { + var $w = $(widget); + var wgd = $w.coords().grid; + var can_go_up = self.can_go_widget_up(wgd); + if (can_go_up && can_go_up !== wgd.row) { + self.move_widget_to($w, can_go_up); + } + }); + + return this; + }; + + + /** + * Move up the specified widget and all below it. + * + * @method move_widget_up + * @param {HTMLElement} $widget The widget you want to move. + * @param {Number} [y_units] The number of cells that the widget has to move. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.move_widget_up = function($widget, y_units) { + var el_grid_data = $widget.coords().grid; + var actual_row = el_grid_data.row; + var moved = []; + var can_go_up = true; + y_units || (y_units = 1); + + if (!this.can_go_up($widget)) { return false; } //break; + + this.for_each_column_occupied(el_grid_data, function(col) { + // can_go_up + if ($.inArray($widget, moved) === -1) { + var widget_grid_data = $widget.coords().grid; + var next_row = actual_row - y_units; + next_row = this.can_go_up_to_row( + widget_grid_data, col, next_row); + + if (!next_row) { + return true; + } + + var $next_widgets = this.widgets_below($widget); + + this.remove_from_gridmap(widget_grid_data); + widget_grid_data.row = next_row; + this.add_to_gridmap(widget_grid_data); + $widget.attr('data-row', widget_grid_data.row); + this.$changed = this.$changed.add($widget); + + moved.push($widget); + + $next_widgets.each($.proxy(function(i, widget) { + this.move_widget_up($(widget), y_units); + }, this)); + } + }); + + }; + + + /** + * Move down the specified widget and all below it. + * + * @method move_widget_down + * @param {jQuery} $widget The jQuery object representing the widget + * you want to move. + * @param {Number} y_units The number of cells that the widget has to move. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.move_widget_down = function($widget, y_units) { + var el_grid_data, actual_row, moved, y_diff; + + if (y_units <= 0) { return false; } + + el_grid_data = $widget.coords().grid; + actual_row = el_grid_data.row; + moved = []; + y_diff = y_units; + + if (!$widget) { return false; } + + if ($.inArray($widget, moved) === -1) { + + var widget_grid_data = $widget.coords().grid; + var next_row = actual_row + y_units; + var $next_widgets = this.widgets_below($widget); + + this.remove_from_gridmap(widget_grid_data); + + $next_widgets.each($.proxy(function(i, widget) { + var $w = $(widget); + var wd = $w.coords().grid; + var tmp_y = this.displacement_diff( + wd, widget_grid_data, y_diff); + + if (tmp_y > 0) { + this.move_widget_down($w, tmp_y); + } + }, this)); + + widget_grid_data.row = next_row; + this.update_widget_position(widget_grid_data, $widget); + $widget.attr('data-row', widget_grid_data.row); + this.$changed = this.$changed.add($widget); + + moved.push($widget); + } + }; + + + /** + * Check if the widget can move to the specified row, else returns the + * upper row possible. + * + * @method can_go_up_to_row + * @param {Number} widget_grid_data The current grid coords object of the + * widget. + * @param {Number} col The target column. + * @param {Number} row The target row. + * @return {Boolean|Number} Returns the row number if the widget can move + * to the target position, else returns false. + */ + fn.can_go_up_to_row = function(widget_grid_data, col, row) { + var ga = this.gridmap; + var result = true; + var urc = []; // upper_rows_in_columns + var actual_row = widget_grid_data.row; + var r; + + /* generate an array with columns as index and array with + * upper rows empty in the column */ + this.for_each_column_occupied(widget_grid_data, function(tcol) { + var grid_col = ga[tcol]; + urc[tcol] = []; + + r = actual_row; + while (r--) { + if (this.is_empty(tcol, r) && + !this.is_placeholder_in(tcol, r) + ) { + urc[tcol].push(r); + } else { + break; + } + } + + if (!urc[tcol].length) { + result = false; + return true; + } + + }); + + if (!result) { return false; } + + /* get common rows starting from upper position in all the columns + * that widget occupies */ + r = row; + for (r = 1; r < actual_row; r++) { + var common = true; + + for (var uc = 0, ucl = urc.length; uc < ucl; uc++) { + if (urc[uc] && $.inArray(r, urc[uc]) === -1) { + common = false; + } + } + + if (common === true) { + result = r; + break; + } + } + + return result; + }; + + + fn.displacement_diff = function(widget_grid_data, parent_bgd, y_units) { + var actual_row = widget_grid_data.row; + var diffs = []; + var parent_max_y = parent_bgd.row + parent_bgd.size_y; + + this.for_each_column_occupied(widget_grid_data, function(col) { + var temp_y_units = 0; + + for (var r = parent_max_y; r < actual_row; r++) { + if (this.is_empty(col, r)) { + temp_y_units = temp_y_units + 1; + } + } + + diffs.push(temp_y_units); + }); + + var max_diff = Math.max.apply(Math, diffs); + y_units = (y_units - max_diff); + + return y_units > 0 ? y_units : 0; + }; + + + /** + * Get widgets below a widget. + * + * @method widgets_below + * @param {HTMLElement} $el The jQuery wrapped HTMLElement. + * @return {jQuery} A jQuery collection of HTMLElements. + */ + fn.widgets_below = function($el) { + var el_grid_data = $.isPlainObject($el) ? $el : $el.coords().grid; + var self = this; + var ga = this.gridmap; + var next_row = el_grid_data.row + el_grid_data.size_y - 1; + var $nexts = $([]); + + this.for_each_column_occupied(el_grid_data, function(col) { + self.for_each_widget_below(col, next_row, function(tcol, trow) { + if (!self.is_player(this) && $.inArray(this, $nexts) === -1) { + $nexts = $nexts.add(this); + return true; // break + } + }); + }); + + return Gridster.sort_by_row_asc($nexts); + }; + + + /** + * Update the array of mapped positions with the new player position. + * + * @method set_cells_player_occupies + * @param {Number} col The new player col. + * @param {Number} col The new player row. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.set_cells_player_occupies = function(col, row) { + this.remove_from_gridmap(this.placeholder_grid_data); + this.placeholder_grid_data.col = col; + this.placeholder_grid_data.row = row; + this.add_to_gridmap(this.placeholder_grid_data, this.$player); + return this; + }; + + + /** + * Remove from the array of mapped positions the reference to the player. + * + * @method empty_cells_player_occupies + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.empty_cells_player_occupies = function() { + this.remove_from_gridmap(this.placeholder_grid_data); + return this; + }; + + + fn.can_go_up = function($el) { + var el_grid_data = $el.coords().grid; + var initial_row = el_grid_data.row; + var prev_row = initial_row - 1; + var ga = this.gridmap; + var upper_rows_by_column = []; + + var result = true; + if (initial_row === 1) { return false; } + + this.for_each_column_occupied(el_grid_data, function(col) { + var $w = this.is_widget(col, prev_row); + + if (this.is_occupied(col, prev_row) || + this.is_player(col, prev_row) || + this.is_placeholder_in(col, prev_row) || + this.is_player_in(col, prev_row) + ) { + result = false; + return true; //break + } + }); + + return result; + }; + + + /** + * Check if it's possible to move a widget to a specific col/row. It takes + * into account the dimensions (`size_y` and `size_x` attrs. of the grid + * coords object) the widget occupies. + * + * @method can_move_to + * @param {Object} widget_grid_data The grid coords object that represents + * the widget. + * @param {Object} col The col to check. + * @param {Object} row The row to check. + * @param {Number} [max_row] The max row allowed. + * @return {Boolean} Returns true if all cells are empty, else return false. + */ + fn.can_move_to = function(widget_grid_data, col, row, max_row) { + var ga = this.gridmap; + var $w = widget_grid_data.el; + var future_wd = { + size_y: widget_grid_data.size_y, + size_x: widget_grid_data.size_x, + col: col, + row: row + }; + var result = true; + + //Prevents widgets go out of the grid + var right_col = col + widget_grid_data.size_x - 1; + if (right_col > this.cols) { + return false; + } + + if (max_row && max_row < row + widget_grid_data.size_y - 1) { + return false; + } + + this.for_each_cell_occupied(future_wd, function(tcol, trow) { + var $tw = this.is_widget(tcol, trow); + if ($tw && (!widget_grid_data.el || $tw.is($w))) { + result = false; + } + }); + + return result; + }; + + + /** + * Given the leftmost column returns all columns that are overlapping + * with the player. + * + * @method get_targeted_columns + * @param {Number} [from_col] The leftmost column. + * @return {Array} Returns an array with column numbers. + */ + fn.get_targeted_columns = function(from_col) { + var max = (from_col || this.player_grid_data.col) + + (this.player_grid_data.size_x - 1); + var cols = []; + for (var col = from_col; col <= max; col++) { + cols.push(col); + } + return cols; + }; + + + /** + * Given the upper row returns all rows that are overlapping with the player. + * + * @method get_targeted_rows + * @param {Number} [from_row] The upper row. + * @return {Array} Returns an array with row numbers. + */ + fn.get_targeted_rows = function(from_row) { + var max = (from_row || this.player_grid_data.row) + + (this.player_grid_data.size_y - 1); + var rows = []; + for (var row = from_row; row <= max; row++) { + rows.push(row); + } + return rows; + }; + + /** + * Get all columns and rows that a widget occupies. + * + * @method get_cells_occupied + * @param {Object} el_grid_data The grid coords object of the widget. + * @return {Object} Returns an object like `{ cols: [], rows: []}`. + */ + fn.get_cells_occupied = function(el_grid_data) { + var cells = { cols: [], rows: []}; + var i; + if (arguments[1] instanceof $) { + el_grid_data = arguments[1].coords().grid; + } + + for (i = 0; i < el_grid_data.size_x; i++) { + var col = el_grid_data.col + i; + cells.cols.push(col); + } + + for (i = 0; i < el_grid_data.size_y; i++) { + var row = el_grid_data.row + i; + cells.rows.push(row); + } + + return cells; + }; + + + /** + * Iterate over the cells occupied by a widget executing a function for + * each one. + * + * @method for_each_cell_occupied + * @param {Object} el_grid_data The grid coords object that represents the + * widget. + * @param {Function} callback The function to execute on each column + * iteration. Column and row are passed as arguments. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.for_each_cell_occupied = function(grid_data, callback) { + this.for_each_column_occupied(grid_data, function(col) { + this.for_each_row_occupied(grid_data, function(row) { + callback.call(this, col, row); + }); + }); + return this; + }; + + + /** + * Iterate over the columns occupied by a widget executing a function for + * each one. + * + * @method for_each_column_occupied + * @param {Object} el_grid_data The grid coords object that represents + * the widget. + * @param {Function} callback The function to execute on each column + * iteration. The column number is passed as first argument. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.for_each_column_occupied = function(el_grid_data, callback) { + for (var i = 0; i < el_grid_data.size_x; i++) { + var col = el_grid_data.col + i; + callback.call(this, col, el_grid_data); + } + }; + + + /** + * Iterate over the rows occupied by a widget executing a function for + * each one. + * + * @method for_each_row_occupied + * @param {Object} el_grid_data The grid coords object that represents + * the widget. + * @param {Function} callback The function to execute on each column + * iteration. The row number is passed as first argument. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.for_each_row_occupied = function(el_grid_data, callback) { + for (var i = 0; i < el_grid_data.size_y; i++) { + var row = el_grid_data.row + i; + callback.call(this, row, el_grid_data); + } + }; + + + + fn._traversing_widgets = function(type, direction, col, row, callback) { + var ga = this.gridmap; + if (!ga[col]) { return; } + + var cr, max; + var action = type + '/' + direction; + if (arguments[2] instanceof $) { + var el_grid_data = arguments[2].coords().grid; + col = el_grid_data.col; + row = el_grid_data.row; + callback = arguments[3]; + } + var matched = []; + var trow = row; + + + var methods = { + 'for_each/above': function() { + while (trow--) { + if (trow > 0 && this.is_widget(col, trow) && + $.inArray(ga[col][trow], matched) === -1 + ) { + cr = callback.call(ga[col][trow], col, trow); + matched.push(ga[col][trow]); + if (cr) { break; } + } + } + }, + 'for_each/below': function() { + for (trow = row + 1, max = ga[col].length; trow < max; trow++) { + if (this.is_widget(col, trow) && + $.inArray(ga[col][trow], matched) === -1 + ) { + cr = callback.call(ga[col][trow], col, trow); + matched.push(ga[col][trow]); + if (cr) { break; } + } + } + } + }; + + if (methods[action]) { + methods[action].call(this); + } + }; + + + /** + * Iterate over each widget above the column and row specified. + * + * @method for_each_widget_above + * @param {Number} col The column to start iterating. + * @param {Number} row The row to start iterating. + * @param {Function} callback The function to execute on each widget + * iteration. The value of `this` inside the function is the jQuery + * wrapped HTMLElement. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.for_each_widget_above = function(col, row, callback) { + this._traversing_widgets('for_each', 'above', col, row, callback); + return this; + }; + + + /** + * Iterate over each widget below the column and row specified. + * + * @method for_each_widget_below + * @param {Number} col The column to start iterating. + * @param {Number} row The row to start iterating. + * @param {Function} callback The function to execute on each widget + * iteration. The value of `this` inside the function is the jQuery wrapped + * HTMLElement. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.for_each_widget_below = function(col, row, callback) { + this._traversing_widgets('for_each', 'below', col, row, callback); + return this; + }; + + + /** + * Returns the highest occupied cell in the grid. + * + * @method get_highest_occupied_cell + * @return {Object} Returns an object with `col` and `row` numbers. + */ + fn.get_highest_occupied_cell = function() { + var r; + var gm = this.gridmap; + var rl = gm[1].length; + var rows = [], cols = []; + var row_in_col = []; + for (var c = gm.length - 1; c >= 1; c--) { + for (r = rl - 1; r >= 1; r--) { + if (this.is_widget(c, r)) { + rows.push(r); + cols.push(c); + break; + } + } + } + + return { + col: Math.max.apply(Math, cols), + row: Math.max.apply(Math, rows) + }; + }; + + + fn.get_widgets_from = function(col, row) { + var ga = this.gridmap; + var $widgets = $(); + + if (col) { + $widgets = $widgets.add( + this.$widgets.filter(function() { + var tcol = $(this).attr('data-col'); + return (tcol === col || tcol > col); + }) + ); + } + + if (row) { + $widgets = $widgets.add( + this.$widgets.filter(function() { + var trow = $(this).attr('data-row'); + return (trow === row || trow > row); + }) + ); + } + + return $widgets; + }; + + + /** + * Set the current height of the parent grid. + * + * @method set_dom_grid_height + * @return {Object} Returns the instance of the Gridster class. + */ + fn.set_dom_grid_height = function(height) { + if (typeof height === 'undefined') { + var r = this.get_highest_occupied_cell().row; + height = r * this.min_widget_height; + } + + this.container_height = height; + this.$el.css('height', this.container_height); + return this; + }; + + /** + * Set the current width of the parent grid. + * + * @method set_dom_grid_width + * @return {Object} Returns the instance of the Gridster class. + */ + fn.set_dom_grid_width = function(cols) { + if (typeof cols === 'undefined') { + cols = this.get_highest_occupied_cell().col; + } + + var max_cols = (this.options.autogrow_cols ? this.options.max_cols : + this.cols); + + cols = Math.min(max_cols, Math.max(cols, this.options.min_cols)); + this.container_width = cols * this.min_widget_width; + this.$el.css('width', this.container_width); + return this; + }; + + + /** + * It generates the neccessary styles to position the widgets. + * + * @method generate_stylesheet + * @param {Number} rows Number of columns. + * @param {Number} cols Number of rows. + * @return {Object} Returns the instance of the Gridster class. + */ + fn.generate_stylesheet = function(opts) { + var styles = ''; + var max_size_x = this.options.max_size_x || this.cols; + var max_rows = 0; + var max_cols = 0; + var i; + var rules; + + opts || (opts = {}); + opts.cols || (opts.cols = this.cols); + opts.rows || (opts.rows = this.rows); + opts.namespace || (opts.namespace = this.options.namespace); + opts.widget_base_dimensions || + (opts.widget_base_dimensions = this.options.widget_base_dimensions); + opts.widget_margins || + (opts.widget_margins = this.options.widget_margins); + opts.min_widget_width = (opts.widget_margins[0] * 2) + + opts.widget_base_dimensions[0]; + opts.min_widget_height = (opts.widget_margins[1] * 2) + + opts.widget_base_dimensions[1]; + + // don't duplicate stylesheets for the same configuration + var serialized_opts = $.param(opts); + if ($.inArray(serialized_opts, Gridster.generated_stylesheets) >= 0) { + return false; + } + + this.generated_stylesheets.push(serialized_opts); + Gridster.generated_stylesheets.push(serialized_opts); + + /* generate CSS styles for cols */ + for (i = opts.cols; i >= 0; i--) { + styles += (opts.namespace + ' [data-col="'+ (i + 1) + '"] { left:' + + ((i * opts.widget_base_dimensions[0]) + + (i * opts.widget_margins[0]) + + ((i + 1) * opts.widget_margins[0])) + 'px; }\n'); + } + + /* generate CSS styles for rows */ + for (i = opts.rows; i >= 0; i--) { + styles += (opts.namespace + ' [data-row="' + (i + 1) + '"] { top:' + + ((i * opts.widget_base_dimensions[1]) + + (i * opts.widget_margins[1]) + + ((i + 1) * opts.widget_margins[1]) ) + 'px; }\n'); + } + + for (var y = 1; y <= opts.rows; y++) { + styles += (opts.namespace + ' [data-sizey="' + y + '"] { height:' + + (y * opts.widget_base_dimensions[1] + + (y - 1) * (opts.widget_margins[1] * 2)) + 'px; }\n'); + } + + for (var x = 1; x <= max_size_x; x++) { + styles += (opts.namespace + ' [data-sizex="' + x + '"] { width:' + + (x * opts.widget_base_dimensions[0] + + (x - 1) * (opts.widget_margins[0] * 2)) + 'px; }\n'); + } + + this.remove_style_tags(); + + return this.add_style_tag(styles); + }; + + + /** + * Injects the given CSS as string to the head of the document. + * + * @method add_style_tag + * @param {String} css The styles to apply. + * @return {Object} Returns the instance of the Gridster class. + */ + fn.add_style_tag = function(css) { + var d = document; + var tag = d.createElement('style'); + + d.getElementsByTagName('head')[0].appendChild(tag); + tag.setAttribute('type', 'text/css'); + + if (tag.styleSheet) { + tag.styleSheet.cssText = css; + } else { + tag.appendChild(document.createTextNode(css)); + } + + this.$style_tags = this.$style_tags.add(tag); + + return this; + }; + + + /** + * Remove the style tag with the associated id from the head of the document + * + * @method remove_style_tag + * @return {Object} Returns the instance of the Gridster class. + */ + fn.remove_style_tags = function() { + var all_styles = Gridster.generated_stylesheets; + var ins_styles = this.generated_stylesheets; + + this.$style_tags.remove(); + + Gridster.generated_stylesheets = $.map(all_styles, function(s) { + if ($.inArray(s, ins_styles) === -1) { return s; } + }); + }; + + + /** + * Generates a faux grid to collide with it when a widget is dragged and + * detect row or column that we want to go. + * + * @method generate_faux_grid + * @param {Number} rows Number of columns. + * @param {Number} cols Number of rows. + * @return {Object} Returns the instance of the Gridster class. + */ + fn.generate_faux_grid = function(rows, cols) { + this.faux_grid = []; + this.gridmap = []; + var col; + var row; + for (col = cols; col > 0; col--) { + this.gridmap[col] = []; + for (row = rows; row > 0; row--) { + this.add_faux_cell(row, col); + } + } + return this; + }; + + + /** + * Add cell to the faux grid. + * + * @method add_faux_cell + * @param {Number} row The row for the new faux cell. + * @param {Number} col The col for the new faux cell. + * @return {Object} Returns the instance of the Gridster class. + */ + fn.add_faux_cell = function(row, col) { + var coords = $({ + left: this.baseX + ((col - 1) * this.min_widget_width), + top: this.baseY + (row -1) * this.min_widget_height, + width: this.min_widget_width, + height: this.min_widget_height, + col: col, + row: row, + original_col: col, + original_row: row + }).coords(); + + if (!$.isArray(this.gridmap[col])) { + this.gridmap[col] = []; + } + + this.gridmap[col][row] = false; + this.faux_grid.push(coords); + + return this; + }; + + + /** + * Add rows to the faux grid. + * + * @method add_faux_rows + * @param {Number} rows The number of rows you want to add to the faux grid. + * @return {Object} Returns the instance of the Gridster class. + */ + fn.add_faux_rows = function(rows) { + var actual_rows = this.rows; + var max_rows = actual_rows + (rows || 1); + + for (var r = max_rows; r > actual_rows; r--) { + for (var c = this.cols; c >= 1; c--) { + this.add_faux_cell(r, c); + } + } + + this.rows = max_rows; + + if (this.options.autogenerate_stylesheet) { + this.generate_stylesheet(); + } + + return this; + }; + + /** + * Add cols to the faux grid. + * + * @method add_faux_cols + * @param {Number} cols The number of cols you want to add to the faux grid. + * @return {Object} Returns the instance of the Gridster class. + */ + fn.add_faux_cols = function(cols) { + var actual_cols = this.cols; + var max_cols = actual_cols + (cols || 1); + max_cols = Math.min(max_cols, this.options.max_cols); + + for (var c = actual_cols + 1; c <= max_cols; c++) { + for (var r = this.rows; r >= 1; r--) { + this.add_faux_cell(r, c); + } + } + + this.cols = max_cols; + + if (this.options.autogenerate_stylesheet) { + this.generate_stylesheet(); + } + + return this; + }; + + + /** + * Recalculates the offsets for the faux grid. You need to use it when + * the browser is resized. + * + * @method recalculate_faux_grid + * @return {Object} Returns the instance of the Gridster class. + */ + fn.recalculate_faux_grid = function() { + var aw = this.$wrapper.width(); + this.baseX = ($(window).width() - aw) / 2; + this.baseY = this.$wrapper.offset().top; + + $.each(this.faux_grid, $.proxy(function(i, coords) { + this.faux_grid[i] = coords.update({ + left: this.baseX + (coords.data.col -1) * this.min_widget_width, + top: this.baseY + (coords.data.row -1) * this.min_widget_height + }); + }, this)); + + return this; + }; + + + /** + * Get all widgets in the DOM and register them. + * + * @method get_widgets_from_DOM + * @return {Object} Returns the instance of the Gridster class. + */ + fn.get_widgets_from_DOM = function() { + var widgets_coords = this.$widgets.map($.proxy(function(i, widget) { + var $w = $(widget); + return this.dom_to_coords($w); + }, this)); + + widgets_coords = Gridster.sort_by_row_and_col_asc(widgets_coords); + + var changes = $(widgets_coords).map($.proxy(function(i, wgd) { + return this.register_widget(wgd) || null; + }, this)); + + if (changes.length) { + this.$el.trigger('gridster:positionschanged'); + } + + return this; + }; + + + /** + * Calculate columns and rows to be set based on the configuration + * parameters, grid dimensions, etc ... + * + * @method generate_grid_and_stylesheet + * @return {Object} Returns the instance of the Gridster class. + */ + fn.generate_grid_and_stylesheet = function() { + var aw = this.$wrapper.width(); + var max_cols = this.options.max_cols; + + var cols = Math.floor(aw / this.min_widget_width) + + this.options.extra_cols; + + var actual_cols = this.$widgets.map(function() { + return $(this).attr('data-col'); + }).get(); + + //needed to pass tests with phantomjs + actual_cols.length || (actual_cols = [0]); + + var min_cols = Math.max.apply(Math, actual_cols); + + this.cols = Math.max(min_cols, cols, this.options.min_cols); + + if (max_cols !== Infinity && max_cols >= min_cols && max_cols < this.cols) { + this.cols = max_cols; + } + + // get all rows that could be occupied by the current widgets + var max_rows = this.options.extra_rows; + this.$widgets.each(function(i, w) { + max_rows += (+$(w).attr('data-sizey')); + }); + + this.rows = Math.max(max_rows, this.options.min_rows); + + this.baseX = ($(window).width() - aw) / 2; + this.baseY = this.$wrapper.offset().top; + + if (this.options.autogenerate_stylesheet) { + this.generate_stylesheet(); + } + + return this.generate_faux_grid(this.rows, this.cols); + }; + + /** + * Destroy this gridster by removing any sign of its presence, making it easy to avoid memory leaks + * + * @method destroy + * @param {Boolean} remove If true, remove gridster from DOM. + * @return {Object} Returns the instance of the Gridster class. + */ + fn.destroy = function(remove) { + this.$el.removeData('gridster'); + + // remove bound callback on window resize + $(window).unbind('.gridster'); + + if (this.drag_api) { + this.drag_api.destroy(); + } + + this.remove_style_tags(); + + remove && this.$el.remove(); + + return this; + }; + + + //jQuery adapter + $.fn.gridster = function(options) { + return this.each(function() { + if (! $(this).data('gridster')) { + $(this).data('gridster', new Gridster( this, options )); + } + }); + }; + + return Gridster; + +})); diff --git a/GRID/src/utils.js b/GRID/src/utils.js new file mode 100644 index 0000000..f66910d --- /dev/null +++ b/GRID/src/utils.js @@ -0,0 +1,72 @@ +;(function(window, undefined) { + + /* Delay, debounce and throttle functions taken from underscore.js + * + * Copyright (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and + * Investigative Reporters & Editors + * + * 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. + */ + + window.delay = function(func, wait) { + var args = Array.prototype.slice.call(arguments, 2); + return setTimeout(function(){ return func.apply(null, args); }, wait); + }; + + window.debounce = function(func, wait, immediate) { + var timeout; + return function() { + var context = this, args = arguments; + var later = function() { + timeout = null; + if (!immediate) func.apply(context, args); + }; + if (immediate && !timeout) func.apply(context, args); + clearTimeout(timeout); + timeout = setTimeout(later, wait); + }; + }; + + window.throttle = function(func, wait) { + var context, args, timeout, throttling, more, result; + var whenDone = debounce( + function(){ more = throttling = false; }, wait); + return function() { + context = this; args = arguments; + var later = function() { + timeout = null; + if (more) func.apply(context, args); + whenDone(); + }; + if (!timeout) timeout = setTimeout(later, wait); + if (throttling) { + more = true; + } else { + result = func.apply(context, args); + } + whenDone(); + throttling = true; + return result; + }; + }; + +})(window); diff --git a/Pic/sickrage.png b/Pic/sickrage.png index 80b76dd2c794fde1c67ba04af62fac8296c92c02..40446fee71f597c8602624f90c7e033b4ce3124d 100644 GIT binary patch literal 130497 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�GZmIaZsSss#-p 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 literal 116971 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

      <&rGeY%uIRz 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 z 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%0AxLy`W&YhXEb-sp;gd!@v7$IszOG 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 QY5)KL07*qoM6N<$f?yP-X8-^I diff --git a/assets/css/demo.css b/assets/css/demo.css new file mode 100644 index 0000000..c64eef7 --- /dev/null +++ b/assets/css/demo.css @@ -0,0 +1,69 @@ +.gridster * { + margin:0; + padding:0; +} + +ul { + list-style-type: none; +} + + +/*/ +/* demo +/*/ + + +body { + font-size: 16px; + font-family: 'Helvetica Neue', Arial, sans-serif; + color: #444; + margin: 30px 40px; +} + +.controls { + margin-bottom: 20px; +} + +/*/ +/* gridster +/*/ + +.gridster ul { + background-color: #EFEFEF; +} + +.gridster li { + font-size: 1em; + font-weight: bold; + text-align: center; + line-height: 100%; +} + + +.gridster { + margin: 0 auto; + + opacity: .8; + + -webkit-transition: opacity .6s; + -moz-transition: opacity .6s; + -o-transition: opacity .6s; + -ms-transition: opacity .6s; + transition: opacity .6s; +} + +.gridster .gs-w { + background: #DDD; + cursor: pointer; +} + +.gridster .player { + background: #BBB; +} + + +.gridster .preview-holder { + border: none!important; + background: red!important; +} + diff --git a/assets/css/grid.css b/assets/css/grid.css new file mode 100644 index 0000000..c64eef7 --- /dev/null +++ b/assets/css/grid.css @@ -0,0 +1,69 @@ +.gridster * { + margin:0; + padding:0; +} + +ul { + list-style-type: none; +} + + +/*/ +/* demo +/*/ + + +body { + font-size: 16px; + font-family: 'Helvetica Neue', Arial, sans-serif; + color: #444; + margin: 30px 40px; +} + +.controls { + margin-bottom: 20px; +} + +/*/ +/* gridster +/*/ + +.gridster ul { + background-color: #EFEFEF; +} + +.gridster li { + font-size: 1em; + font-weight: bold; + text-align: center; + line-height: 100%; +} + + +.gridster { + margin: 0 auto; + + opacity: .8; + + -webkit-transition: opacity .6s; + -moz-transition: opacity .6s; + -o-transition: opacity .6s; + -ms-transition: opacity .6s; + transition: opacity .6s; +} + +.gridster .gs-w { + background: #DDD; + cursor: pointer; +} + +.gridster .player { + background: #BBB; +} + + +.gridster .preview-holder { + border: none!important; + background: red!important; +} + diff --git a/assets/css/jquery.gridster.css b/assets/css/jquery.gridster.css new file mode 100644 index 0000000..c36d418 --- /dev/null +++ b/assets/css/jquery.gridster.css @@ -0,0 +1,64 @@ +/*! gridster.js - v0.1.0 - 2012-10-20 +* http://gridster.net/ +* Copyright (c) 2012 ducksboard; Licensed MIT */ + +.gridster { + position:relative; +} + +.gridster > * { + margin: 0 auto; + -webkit-transition: height .4s; + -moz-transition: height .4s; + -o-transition: height .4s; + -ms-transition: height .4s; + transition: height .4s; +} + +.gridster .gs_w{ + z-index: 2; + position: absolute; +} + +.ready .gs_w:not(.preview-holder) { + -webkit-transition: opacity .3s, left .3s, top .3s; + -moz-transition: opacity .3s, left .3s, top .3s; + -o-transition: opacity .3s, left .3s, top .3s; + transition: opacity .3s, left .3s, top .3s; +} + +.ready .gs_w:not(.preview-holder) { + -webkit-transition: opacity .3s, left .3s, top .3s, width .3s, height .3s; + -moz-transition: opacity .3s, left .3s, top .3s, width .3s, height .3s; + -o-transition: opacity .3s, left .3s, top .3s, width .3s, height .3s; + transition: opacity .3s, left .3s, top .3s, width .3s, height .3s; +} + +.gridster .preview-holder { + z-index: 1; + position: absolute; + background-color: #fff; + border-color: #fff; + opacity: 0.3; +} + +.gridster .player-revert { + z-index: 10!important; + -webkit-transition: left .3s, top .3s!important; + -moz-transition: left .3s, top .3s!important; + -o-transition: left .3s, top .3s!important; + transition: left .3s, top .3s!important; +} + +.gridster .dragging { + z-index: 10!important; + -webkit-transition: all 0s !important; + -moz-transition: all 0s !important; + -o-transition: all 0s !important; + transition: all 0s !important; +} + +/* Uncomment this if you set helper : "clone" in draggable options */ +/*.gridster .player { + opacity:0; +}*/ diff --git a/assets/css/jquery.gridster.min.css b/assets/css/jquery.gridster.min.css new file mode 100644 index 0000000..f11001c --- /dev/null +++ b/assets/css/jquery.gridster.min.css @@ -0,0 +1,3 @@ +/*! gridster.js - v0.1.0 - 2012-10-20 +* http://gridster.net/ +* Copyright (c) 2012 ducksboard; Licensed MIT */.gridster{position:relative}.gridster>*{margin:0 auto;-webkit-transition:height .4s;-moz-transition:height .4s;-o-transition:height .4s;-ms-transition:height .4s;transition:height .4s}.gridster .gs_w{z-index:2;position:absolute}.ready .gs_w:not(.preview-holder){-webkit-transition:opacity .3s,left .3s,top .3s;-moz-transition:opacity .3s,left .3s,top .3s;-o-transition:opacity .3s,left .3s,top .3s;transition:opacity .3s,left .3s,top .3s}.ready .gs_w:not(.preview-holder){-webkit-transition:opacity .3s,left .3s,top .3s,width .3s,height .3s;-moz-transition:opacity .3s,left .3s,top .3s,width .3s,height .3s;-o-transition:opacity .3s,left .3s,top .3s,width .3s,height .3s;transition:opacity .3s,left .3s,top .3s,width .3s,height .3s}.gridster .preview-holder{z-index:1;position:absolute;background-color:#fff;border-color:#fff;opacity:.3}.gridster .player-revert{z-index:10!important;-webkit-transition:left .3s,top .3s!important;-moz-transition:left .3s,top .3s!important;-o-transition:left .3s,top .3s!important;transition:left .3s,top .3s!important}.gridster .dragging{z-index:10!important;-webkit-transition:all 0s!important;-moz-transition:all 0s!important;-o-transition:all 0s!important;transition:all 0s!important} \ No newline at end of file diff --git a/assets/css/styles.css b/assets/css/styles.css new file mode 100644 index 0000000..ec0ce7f --- /dev/null +++ b/assets/css/styles.css @@ -0,0 +1,60 @@ +body { + background-color: #EEEEEE; + font-family: 'Helvetica Neue', Arial, sans-serif; + -webkit-font-smoothing: antialiased; + font-size: x-small; + color: #666666; +} + +ul, ol { + list-style: none; + margin:0px auto 0px auto; + text-align: center; + vertical-align: middle; + + +} + +h1 { + margin-bottom: 12px; + text-align: center; + font-size: 30px; + font-weight: 400; +} + +h3 { + font-size: 25px; + font-weight: 600; + color: white; +} + +/* Gridster styles */ +.demo { + margin: 3em 0; + padding: 7.5em 0 5.5em; + /* background: #004756; */ +} + +.demo:hover .gridster { + opacity: 1; +} + +.gridster { + width: 940px; + margin: 0 auto; + + opacity: .8; + + -webkit-transition: opacity .6s; + -moz-transition: opacity .6s; + -o-transition: opacity .6s; + -ms-transition: opacity .6s; + transition: opacity .6s; +} + +.gridster .gs_w { + /* background: #FFF; */ + cursor: pointer; + -webkit-box-shadow: 0 0 5px rgba(0,0,0,0.3); + /* box-shadow: 0 0 5px rgba(0,0,0,0.3); */ +} \ No newline at end of file diff --git a/assets/gridster_new.js b/assets/gridster_new.js new file mode 100644 index 0000000..aa63834 --- /dev/null +++ b/assets/gridster_new.js @@ -0,0 +1,107 @@ + + $(window).ready(function () { + var localData = JSON.parse(localStorage.getItem('positions')); + + if(localData!=null) + { + $.each(localData, function(i,value){ + + var id_name; + + id_name="#"; + id_name = id_name + value.id; + console.log(id_name); + + $(id_name).attr({"data-col":value.col, "data-row":value.row, "data-sizex":value.size_x, "data-sizey":value.size_y}); + + +}); + } + else{ + console.log('No data returned by the server'); + } + + + // widget_selector: "> ul" + // Define which elements are the widgets. Can be a CSS Selector string or a jQuery collection of HTMLElements. + + // widget_margins: [3, 3] + // Horizontal and vertical margins respectively for widgets. + + // widget_base_dimensions: [110, 110] + // Base widget dimensions in pixels. The first index is the width, the second is the height. + + var grid_canvas = $(".gridster > ul").gridster({ + widget_margins: [3, 3], + widget_base_dimensions: [140, 140], + draggable: {ignore_dragging: true}, + max_cols: [4], + max_rows: [4], + + // serialize_params: function($w, wgd) { return { id: $($w).attr('id'),col: wgd.col, row: wgd.row,size_x: wgd.size_x,size_y: wgd.size_y } + // A function to return serialized data for each each widget, used when calling the serialize method. Two arguments are passed: + // $w: the jQuery wrapped HTMLElement which is used to get the id, and wgd: the grid coords object with keys col, row, size_x and size_y. + + + serialize_params: function($w, wgd) + { + return { + id: $($w).attr('id'), + col: wgd.col, + row: wgd.row, + size_x: wgd.size_x, + size_y: wgd.size_y, + + }; + }, + + // draggable.stop: function(event, ui){} -- A callback for when dragging stops. + // You can also implement other draggable options based on your requirements + // draggable.start: function(event, ui){} -- A callback for when dragging starts. + // draggable.drag: function(event, ui){} -- A callback for when the mouse is moved during the dragging. + + draggable: + { + stop: function(event, ui) { + + // .serialize( ) + // Creates an array of objects representing the current position of all widgets in the grid. + // Returns an Array of Objects (ready to be encoded as a JSON string) with the data specified by the serialize_params option + // JSON.stringify() converts a primitive value, object or array to a JSON-formatted string that can later be parsed with JSON.parse(). + + var positions = JSON.stringify(this.serialize()); + + // With HTML5, web pages can store data locally within the user's browser. + // Earlier, this was done with cookies. However, Web Storage is more secure and faster. + // The data is not included with every server request, but used ONLY when asked for. + // It is also possible to store large amounts of data, without affecting the website's performance. + // The data is stored in key/value pairs, and a web page can only access data stored by itself. + + localStorage.setItem('positions', positions); + + + $.post( + "process.php", + {"positions": positions}, + function(data) + { + + // this is where you can check if your data is sent to the server or not. + // A status of 200 implies success + + console.log(data); + if(data==200) + console.log("Data successfully sent to the server"); + else + console.log + } + ); + + } + } + }).data('gridster'); + + + +}); + \ No newline at end of file diff --git a/assets/jquery.gridster.js b/assets/jquery.gridster.js new file mode 100644 index 0000000..947f4bb --- /dev/null +++ b/assets/jquery.gridster.js @@ -0,0 +1,3232 @@ +/*! gridster.js - v0.1.0 - 2012-10-20 +* http://gridster.net/ +* Copyright (c) 2012 ducksboard; Licensed MIT */ + +;(function($, window, document, undefined){ + /** + * Creates objects with coordinates (x1, y1, x2, y2, cx, cy, width, height) + * to simulate DOM elements on the screen. + * Coords is used by Gridster to create a faux grid with any DOM element can + * collide. + * + * @class Coords + * @param {HTMLElement|Object} obj The jQuery HTMLElement or a object with: left, + * top, width and height properties. + * @return {Object} Coords instance. + * @constructor + */ + function Coords(obj) { + if (obj[0] && $.isPlainObject(obj[0])) { + this.data = obj[0]; + }else { + this.el = obj; + } + + this.isCoords = true; + this.coords = {}; + this.init(); + return this; + } + + + var fn = Coords.prototype; + + + fn.init = function(){ + this.set(); + this.original_coords = this.get(); + }; + + + fn.set = function(update, not_update_offsets) { + var el = this.el; + + if (el && !update) { + this.data = el.offset(); + this.data.width = el.width(); + this.data.height = el.height(); + } + + if (el && update && !not_update_offsets) { + var offset = el.offset(); + this.data.top = offset.top; + this.data.left = offset.left; + } + + var d = this.data; + + this.coords.x1 = d.left; + this.coords.y1 = d.top; + this.coords.x2 = d.left + d.width; + this.coords.y2 = d.top + d.height; + this.coords.cx = d.left + (d.width / 2); + this.coords.cy = d.top + (d.height / 2); + this.coords.width = d.width; + this.coords.height = d.height; + this.coords.el = el || false ; + + return this; + }; + + + fn.update = function(data){ + if (!data && !this.el) { + return this; + } + + if (data) { + var new_data = $.extend({}, this.data, data); + this.data = new_data; + return this.set(true, true); + } + + this.set(true); + return this; + }; + + + fn.get = function(){ + return this.coords; + }; + + + //jQuery adapter + $.fn.coords = function() { + if (this.data('coords') ) { + return this.data('coords'); + } + + var ins = new Coords(this, arguments[0]); + this.data('coords', ins); + return ins; + }; + +}(jQuery, window, document)); + +;(function($, window, document, undefined){ + + var defaults = { + colliders_context: document.body + // ,on_overlap: function(collider_data){}, + // on_overlap_start : function(collider_data){}, + // on_overlap_stop : function(collider_data){} + }; + + + /** + * Detects collisions between a DOM element against other DOM elements or + * Coords objects. + * + * @class Collision + * @uses Coords + * @param {HTMLElement} el The jQuery wrapped HTMLElement. + * @param {HTMLElement|Array} colliders Can be a jQuery collection + * of HTMLElements or an Array of Coords instances. + * @param {Object} [options] An Object with all options you want to + * overwrite: + * @param {Function} [options.on_overlap_start] Executes a function the first + * time each `collider ` is overlapped. + * @param {Function} [options.on_overlap_stop] Executes a function when a + * `collider` is no longer collided. + * @param {Function} [options.on_overlap] Executes a function when the + * mouse is moved during the collision. + * @return {Object} Collision instance. + * @constructor + */ + function Collision(el, colliders, options) { + this.options = $.extend(defaults, options); + this.$element = el; + this.last_colliders = []; + this.last_colliders_coords = []; + if (typeof colliders === 'string' || colliders instanceof jQuery) { + this.$colliders = $(colliders, + this.options.colliders_context).not(this.$element); + }else{ + this.colliders = $(colliders); + } + + this.init(); + } + + + var fn = Collision.prototype; + + + fn.init = function() { + this.find_collisions(); + }; + + + fn.overlaps = function(a, b) { + var x = false; + var y = false; + + if ((b.x1 >= a.x1 && b.x1 <= a.x2) || + (b.x2 >= a.x1 && b.x2 <= a.x2) || + (a.x1 >= b.x1 && a.x2 <= b.x2) + ) { x = true; } + + if ((b.y1 >= a.y1 && b.y1 <= a.y2) || + (b.y2 >= a.y1 && b.y2 <= a.y2) || + (a.y1 >= b.y1 && a.y2 <= b.y2) + ) { y = true; } + + return (x && y); + }; + + + fn.detect_overlapping_region = function(a, b){ + var regionX = ''; + var regionY = ''; + + if (a.y1 > b.cy && a.y1 < b.y2) { regionX = 'N'; } + if (a.y2 > b.y1 && a.y2 < b.cy) { regionX = 'S'; } + if (a.x1 > b.cx && a.x1 < b.x2) { regionY = 'W'; } + if (a.x2 > b.x1 && a.x2 < b.cx) { regionY = 'E'; } + + return (regionX + regionY) || 'C'; + }; + + + fn.calculate_overlapped_area_coords = function(a, b){ + var x1 = Math.max(a.x1, b.x1); + var y1 = Math.max(a.y1, b.y1); + var x2 = Math.min(a.x2, b.x2); + var y2 = Math.min(a.y2, b.y2); + + return $({ + left: x1, + top: y1, + width : (x2 - x1), + height: (y2 - y1) + }).coords().get(); + }; + + + fn.calculate_overlapped_area = function(coords){ + return (coords.width * coords.height); + }; + + + fn.manage_colliders_start_stop = function(new_colliders_coords, start_callback, stop_callback){ + var last = this.last_colliders_coords; + + for (var i = 0, il = last.length; i < il; i++) { + if ($.inArray(last[i], new_colliders_coords) === -1) { + start_callback.call(this, last[i]); + } + } + + for (var j = 0, jl = new_colliders_coords.length; j < jl; j++) { + if ($.inArray(new_colliders_coords[j], last) === -1) { + stop_callback.call(this, new_colliders_coords[j]); + } + + } + }; + + + fn.find_collisions = function(player_data_coords){ + var self = this; + var colliders_coords = []; + var colliders_data = []; + var $colliders = (this.colliders || this.$colliders); + var count = $colliders.length; + var player_coords = self.$element.coords() + .update(player_data_coords || false).get(); + + while(count--){ + var $collider = self.$colliders ? + $($colliders[count]) : $colliders[count]; + var $collider_coords_ins = ($collider.isCoords) ? + $collider : $collider.coords(); + var collider_coords = $collider_coords_ins.get(); + var overlaps = self.overlaps(player_coords, collider_coords); + + if (!overlaps) { + continue; + } + + var region = self.detect_overlapping_region( + player_coords, collider_coords); + + //todo: make this an option + if (region === 'C'){ + var area_coords = self.calculate_overlapped_area_coords( + player_coords, collider_coords); + var area = self.calculate_overlapped_area(area_coords); + var collider_data = { + area: area, + area_coords : area_coords, + region: region, + coords: collider_coords, + player_coords: player_coords, + el: $collider + }; + + if (self.options.on_overlap) { + self.options.on_overlap.call(this, collider_data); + } + colliders_coords.push($collider_coords_ins); + colliders_data.push(collider_data); + } + } + + if (self.options.on_overlap_stop || self.options.on_overlap_start) { + this.manage_colliders_start_stop(colliders_coords, + self.options.on_overlap_stop, self.options.on_overlap_start); + } + + this.last_colliders_coords = colliders_coords; + + return colliders_data; + }; + + + fn.get_closest_colliders = function(player_data_coords){ + var colliders = this.find_collisions(player_data_coords); + + colliders.sort(function(a, b) { + /* if colliders are being overlapped by the "C" (center) region, + * we have to set a lower index in the array to which they are placed + * above in the grid. */ + if (a.region === 'C' && b.region === 'C') { + if (a.coords.y1 < b.coords.y1 || a.coords.x1 < b.coords.x1) { + return - 1; + }else{ + return 1; + } + } + + if (a.area < b.area) { + return 1; + } + + return 1; + }); + return colliders; + }; + + + //jQuery adapter + $.fn.collision = function(collider, options) { + return new Collision( this, collider, options ); + }; + + +}(jQuery, window, document)); + +;(function(window, undefined) { + /* Debounce and throttle functions taken from underscore.js */ + window.debounce = function(func, wait, immediate) { + var timeout; + return function() { + var context = this, args = arguments; + var later = function() { + timeout = null; + if (!immediate) func.apply(context, args); + }; + if (immediate && !timeout) func.apply(context, args); + clearTimeout(timeout); + timeout = setTimeout(later, wait); + }; + }; + + + window.throttle = function(func, wait) { + var context, args, timeout, throttling, more, result; + var whenDone = debounce( + function(){ more = throttling = false; }, wait); + return function() { + context = this; args = arguments; + var later = function() { + timeout = null; + if (more) func.apply(context, args); + whenDone(); + }; + if (!timeout) timeout = setTimeout(later, wait); + if (throttling) { + more = true; + } else { + result = func.apply(context, args); + } + whenDone(); + throttling = true; + return result; + }; + }; + +})(window); + +;(function($, window, document, undefined){ + + var defaults = { + items: '.gs_w', + distance: 1, + limit: true, + offset_left: 0, + autoscroll: true, + ignore_dragging: ['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON'], + handle: null + // ,drag: function(e){}, + // start : function(e, ui){}, + // stop : function(e){} + }; + + var $window = $(window); + var isTouch = !!('ontouchstart' in window); + var pointer_events = { + start: isTouch ? 'touchstart' : 'mousedown.draggable', + move: isTouch ? 'touchmove' : 'mousemove.draggable', + end: isTouch ? 'touchend' : 'mouseup.draggable' + }; + + /** + * Basic drag implementation for DOM elements inside a container. + * Provide start/stop/drag callbacks. + * + * @class Draggable + * @param {HTMLElement} el The HTMLelement that contains all the widgets + * to be dragged. + * @param {Object} [options] An Object with all options you want to + * overwrite: + * @param {HTMLElement|String} [options.items] Define who will + * be the draggable items. Can be a CSS Selector String or a + * collection of HTMLElements. + * @param {Number} [options.distance] Distance in pixels after mousedown + * the mouse must move before dragging should start. + * @param {Boolean} [options.limit] Constrains dragging to the width of + * the container + * @param {offset_left} [options.offset_left] Offset added to the item + * that is being dragged. + * @param {Number} [options.drag] Executes a callback when the mouse is + * moved during the dragging. + * @param {Number} [options.start] Executes a callback when the drag + * starts. + * @param {Number} [options.stop] Executes a callback when the drag stops. + * @return {Object} Returns `el`. + * @constructor + */ + function Draggable(el, options) { + this.options = $.extend({}, defaults, options); + this.$body = $(document.body); + this.$container = $(el); + this.$dragitems = $(this.options.items, this.$container); + this.is_dragging = false; + this.player_min_left = 0 + this.options.offset_left; + this.init(); + } + + var fn = Draggable.prototype; + + fn.init = function() { + this.calculate_positions(); + this.$container.css('position', 'relative'); + this.disabled = false; + this.events(); + + $(window).bind('resize', + throttle($.proxy(this.calculate_positions, this), 200)); + }; + + fn.events = function() { + this.$container.on('selectstart', $.proxy(this.on_select_start, this)); + + this.$container.on(pointer_events.start, this.options.items, $.proxy( + this.drag_handler, this)); + + this.$body.on(pointer_events.end, $.proxy(function(e) { + this.is_dragging = false; + if (this.disabled) { return; } + this.$body.off(pointer_events.move); + if (this.drag_start) { + this.on_dragstop(e); + } + }, this)); + }; + + fn.get_actual_pos = function($el) { + var pos = $el.position(); + return pos; + }; + + + fn.get_mouse_pos = function(e) { + if (isTouch) { + var oe = e.originalEvent; + e = oe.touches.length ? oe.touches[0] : oe.changedTouches[0]; + } + + return { + left: e.clientX, + top: e.clientY + }; + }; + + + fn.get_offset = function(e) { + e.preventDefault(); + var mouse_actual_pos = this.get_mouse_pos(e); + var diff_x = Math.round( + mouse_actual_pos.left - this.mouse_init_pos.left); + var diff_y = Math.round(mouse_actual_pos.top - this.mouse_init_pos.top); + + var left = Math.round(this.el_init_offset.left + diff_x - this.baseX); + var top = Math.round( + this.el_init_offset.top + diff_y - this.baseY + this.scrollOffset); + + if (this.options.limit) { + if (left > this.player_max_left) { + left = this.player_max_left; + }else if(left < this.player_min_left) { + left = this.player_min_left; + } + } + + return { + left: left, + top: top, + mouse_left: mouse_actual_pos.left, + mouse_top: mouse_actual_pos.top + }; + }; + + + fn.manage_scroll = function(offset) { + /* scroll document */ + var nextScrollTop; + var scrollTop = $window.scrollTop(); + var min_window_y = scrollTop; + var max_window_y = min_window_y + this.window_height; + + var mouse_down_zone = max_window_y - 50; + var mouse_up_zone = min_window_y + 50; + + var abs_mouse_left = offset.mouse_left; + var abs_mouse_top = min_window_y + offset.mouse_top; + + var max_player_y = (this.doc_height - this.window_height + + this.player_height); + + if (abs_mouse_top >= mouse_down_zone) { + nextScrollTop = scrollTop + 30; + if (nextScrollTop < max_player_y) { + $window.scrollTop(nextScrollTop); + this.scrollOffset = this.scrollOffset + 30; + } + } + + if (abs_mouse_top <= mouse_up_zone) { + nextScrollTop = scrollTop - 30; + if (nextScrollTop > 0) { + $window.scrollTop(nextScrollTop); + this.scrollOffset = this.scrollOffset - 30; + } + } + }; + + + fn.calculate_positions = function(e) { + this.window_height = $window.height(); + }; + + + fn.drag_handler = function(e) { + var node = e.target.nodeName; + if (this.disabled || e.which !== 1 && !isTouch) { + return; + } + + if (this.ignore_drag(e)) { + return; + } + + var self = this; + var first = true; + this.$player = $(e.currentTarget); + + this.el_init_pos = this.get_actual_pos(this.$player); + this.mouse_init_pos = this.get_mouse_pos(e); + this.offsetY = this.mouse_init_pos.top - this.el_init_pos.top; + + this.$body.on(pointer_events.move, function(mme){ + var mouse_actual_pos = self.get_mouse_pos(mme); + var diff_x = Math.abs( + mouse_actual_pos.left - self.mouse_init_pos.left); + var diff_y = Math.abs( + mouse_actual_pos.top - self.mouse_init_pos.top); + if (!(diff_x > self.options.distance || + diff_y > self.options.distance) + ) { + return false; + } + + if (first) { + first = false; + self.on_dragstart.call(self, mme); + return false; + } + + if (self.is_dragging === true) { + self.on_dragmove.call(self, mme); + } + + return false; + }); + + return false; + }; + + + fn.on_dragstart = function(e) { + e.preventDefault(); + this.drag_start = true; + this.is_dragging = true; + var offset = this.$container.offset(); + this.baseX = Math.round(offset.left); + this.baseY = Math.round(offset.top); + this.doc_height = $(document).height(); + + if (this.options.helper === 'clone') { + this.$helper = this.$player.clone() + .appendTo(this.$container).addClass('helper'); + this.helper = true; + }else{ + this.helper = false; + } + this.scrollOffset = 0; + this.el_init_offset = this.$player.offset(); + this.player_width = this.$player.width(); + this.player_height = this.$player.height(); + this.player_max_left = (this.$container.width() - this.player_width + + this.options.offset_left); + + if (this.options.start) { + this.options.start.call(this.$player, e, { + helper: this.helper ? this.$helper : this.$player + }); + } + return false; + }; + + + fn.on_dragmove = function(e) { + var offset = this.get_offset(e); + + this.options.autoscroll && this.manage_scroll(offset); + + (this.helper ? this.$helper : this.$player).css({ + 'position': 'absolute', + 'left' : offset.left, + 'top' : offset.top + }); + + var ui = { + 'position': { + 'left': offset.left, + 'top': offset.top + } + }; + + if (this.options.drag) { + this.options.drag.call(this.$player, e, ui); + } + return false; + }; + + + fn.on_dragstop = function(e) { + var offset = this.get_offset(e); + this.drag_start = false; + + var ui = { + 'position': { + 'left': offset.left, + 'top': offset.top + } + }; + + if (this.options.stop) { + this.options.stop.call(this.$player, e, ui); + } + + if (this.helper) { + this.$helper.remove(); + } + + return false; + }; + + fn.on_select_start = function(e) { + if (this.disabled) { return; } + + if (this.ignore_drag(e)) { + return; + } + + return false; + }; + + fn.enable = function() { + this.disabled = false; + }; + + fn.disable = function() { + this.disabled = true; + }; + + + fn.destroy = function(){ + this.disable(); + $.removeData(this.$container, 'drag'); + }; + + fn.ignore_drag = function(event) { + if (this.options.handle) { + return !$(event.target).is(this.options.handle); + } + + return $.inArray(event.target.nodeName, this.options.ignore_dragging) >= 0; + }; + + //jQuery adapter + $.fn.drag = function ( options ) { + return this.each(function () { + if (!$.data(this, 'drag')) { + $.data(this, 'drag', new Draggable( this, options )); + } + }); + }; + + +}(jQuery, window, document)); + +;(function($, window, document, undefined) { + + var defaults = { + namespace: '', + widget_selector: 'li', + widget_margins: [10, 10], + widget_base_dimensions: [400, 225], + extra_rows: 0, + extra_cols: 0, + min_cols: 1, + min_rows: 15, + max_size_x: 6, + autogenerate_stylesheet: true, + avoid_overlapped_widgets: true, + serialize_params: function($w, wgd) { + return { + col: wgd.col, + row: wgd.row, + size_x: wgd.size_x, + size_y: wgd.size_y + }; + }, + collision: {}, + draggable: { + distance: 4 + } + }; + + + /** + * @class Gridster + * @uses Draggable + * @uses Collision + * @param {HTMLElement} el The HTMLelement that contains all the widgets. + * @param {Object} [options] An Object with all options you want to + * overwrite: + * @param {HTMLElement|String} [options.widget_selector] Define who will + * be the draggable widgets. Can be a CSS Selector String or a + * collection of HTMLElements + * @param {Array} [options.widget_margins] Margin between widgets. + * The first index for the horizontal margin (left, right) and + * the second for the vertical margin (top, bottom). + * @param {Array} [options.widget_base_dimensions] Base widget dimensions + * in pixels. The first index for the width and the second for the + * height. + * @param {Number} [options.extra_cols] Add more columns in addition to + * those that have been calculated. + * @param {Number} [options.extra_rows] Add more rows in addition to + * those that have been calculated. + * @param {Number} [options.min_cols] The minimum required columns. + * @param {Number} [options.min_rows] The minimum required rows. + * @param {Number} [options.max_size_x] The maximum number of columns + * that a widget can span. + * @param {Boolean} [options.autogenerate_stylesheet] If true, all the + * CSS required to position all widgets in their respective columns + * and rows will be generated automatically and injected to the + * `` of the document. You can set this to false, and write + * your own CSS targeting rows and cols via data-attributes like so: + * `[data-col="1"] { left: 10px; }` + * @param {Boolean} [options.avoid_overlapped_widgets] Avoid that widgets loaded + * from the DOM can be overlapped. It is helpful if the positions were + * bad stored in the database or if there was any conflict. + * @param {Function} [options.serialize_params] Return the data you want + * for each widget in the serialization. Two arguments are passed: + * `$w`: the jQuery wrapped HTMLElement, and `wgd`: the grid + * coords object (`col`, `row`, `size_x`, `size_y`). + * @param {Object} [options.collision] An Object with all options for + * Collision class you want to overwrite. See Collision docs for + * more info. + * @param {Object} [options.draggable] An Object with all options for + * Draggable class you want to overwrite. See Draggable docs for more + * info. + * + * @constructor + */ + function Gridster(el, options) { + this.options = $.extend(true, defaults, options); + this.$el = $(el); + this.$wrapper = this.$el.parent(); + this.$widgets = this.$el.children(this.options.widget_selector).addClass('gs_w'); + this.widgets = []; + this.$changed = $([]); + this.wrapper_width = this.$wrapper.width(); + this.min_widget_width = (this.options.widget_margins[0] * 2) + + this.options.widget_base_dimensions[0]; + this.min_widget_height = (this.options.widget_margins[1] * 2) + + this.options.widget_base_dimensions[1]; + this.init(); + } + + Gridster.generated_stylesheets = []; + + var fn = Gridster.prototype; + + fn.init = function() { + this.generate_grid_and_stylesheet(); + this.get_widgets_from_DOM(); + this.set_dom_grid_height(); + this.$wrapper.addClass('ready'); + this.draggable(); + + $(window).bind( + 'resize', throttle($.proxy(this.recalculate_faux_grid, this), 200)); + }; + + + /** + * Disables dragging. + * + * @method disable + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.disable = function() { + this.$wrapper.find('.player-revert').removeClass('player-revert'); + this.drag_api.disable(); + return this; + }; + + + /** + * Enables dragging. + * + * @method enable + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.enable = function() { + this.drag_api.enable(); + return this; + }; + + + /** + * Add a new widget to the grid. + * + * @method add_widget + * @param {String|HTMLElement} html The string representing the HTML of the widget + * or the HTMLElement. + * @param {Number} [size_x] The nº of rows the widget occupies horizontally. + * @param {Number} [size_y] The nº of columns the widget occupies vertically. + * @param {Number} [col] The column the widget should start in. + * @param {Number} [row] The row the widget should start in. + * @return {HTMLElement} Returns the jQuery wrapped HTMLElement representing. + * the widget that was just created. + */ + fn.add_widget = function(html, size_x, size_y, col, row) { + var pos; + size_x || (size_x = 1); + size_y || (size_y = 1); + + if (!col & !row) { + pos = this.next_position(size_x, size_y); + }else{ + pos = { + col: col, + row: row + }; + + this.empty_cells(col, row, size_x, size_y); + } + + var $w = $(html).attr({ + 'data-col': pos.col, + 'data-row': pos.row, + 'data-sizex' : size_x, + 'data-sizey' : size_y + }).addClass('gs_w').appendTo(this.$el).hide(); + + this.$widgets = this.$widgets.add($w); + + this.register_widget($w); + + this.add_faux_rows(pos.size_y); + //this.add_faux_cols(pos.size_x); + + this.set_dom_grid_height(); + + return $w.fadeIn(); + }; + + + + /** + * Change the size of a widget. + * + * @method resize_widget + * @param {HTMLElement} $widget The jQuery wrapped HTMLElement + * representing the widget. + * @param {Number} size_x The number of columns that will occupy the widget. + * @param {Number} size_y The number of rows that will occupy the widget. + * @return {HTMLElement} Returns $widget. + */ + fn.resize_widget = function($widget, size_x, size_y) { + var wgd = $widget.coords().grid; + size_x || (size_x = wgd.size_x); + size_y || (size_y = wgd.size_y); + + if (size_x > this.cols) { + size_x = this.cols; + } + + var old_cells_occupied = this.get_cells_occupied(wgd); + var old_size_x = wgd.size_x; + var old_size_y = wgd.size_y; + var old_col = wgd.col; + var new_col = old_col; + var wider = size_x > old_size_x; + var taller = size_y > old_size_y; + + if (old_col + size_x - 1 > this.cols) { + var diff = old_col + (size_x - 1) - this.cols; + var c = old_col - diff; + new_col = Math.max(1, c); + } + + var new_grid_data = { + col: new_col, + row: wgd.row, + size_x: size_x, + size_y: size_y + }; + + var new_cells_occupied = this.get_cells_occupied(new_grid_data); + + var empty_cols = []; + $.each(old_cells_occupied.cols, function(i, col) { + if ($.inArray(col, new_cells_occupied.cols) === -1) { + empty_cols.push(col); + } + }); + + var occupied_cols = []; + $.each(new_cells_occupied.cols, function(i, col) { + if ($.inArray(col, old_cells_occupied.cols) === -1) { + occupied_cols.push(col); + } + }); + + var empty_rows = []; + $.each(old_cells_occupied.rows, function(i, row) { + if ($.inArray(row, new_cells_occupied.rows) === -1) { + empty_rows.push(row); + } + }); + + var occupied_rows = []; + $.each(new_cells_occupied.rows, function(i, row) { + if ($.inArray(row, old_cells_occupied.rows) === -1) { + occupied_rows.push(row); + } + }); + + this.remove_from_gridmap(wgd); + + if (occupied_cols.length) { + var cols_to_empty = [ + new_col, wgd.row, size_x, Math.min(old_size_y, size_y), $widget + ]; + this.empty_cells.apply(this, cols_to_empty); + } + + if (occupied_rows.length) { + var rows_to_empty = [new_col, wgd.row, size_x, size_y, $widget]; + this.empty_cells.apply(this, rows_to_empty); + } + + wgd.col = new_col; + wgd.size_x = size_x; + wgd.size_y = size_y; + this.add_to_gridmap(new_grid_data, $widget); + + //update coords instance attributes + $widget.data('coords').update({ + width: (size_x * this.options.widget_base_dimensions[0] + + ((size_x - 1) * this.options.widget_margins[0]) * 2), + height: (size_y * this.options.widget_base_dimensions[1] + + ((size_y - 1) * this.options.widget_margins[1]) * 2) + }); + + if (size_y > old_size_y) { + this.add_faux_rows(size_y - old_size_y); + } + + if (size_x > old_size_x) { + this.add_faux_cols(size_x - old_size_x); + } + + $widget.attr({ + 'data-col': new_col, + 'data-sizex': size_x, + 'data-sizey': size_y + }); + + if (empty_cols.length) { + var cols_to_remove_holes = [ + empty_cols[0], wgd.row, + empty_cols.length, + Math.min(old_size_y, size_y), + $widget + ]; + + this.remove_empty_cells.apply(this, cols_to_remove_holes); + } + + if (empty_rows.length) { + var rows_to_remove_holes = [ + new_col, wgd.row, size_x, size_y, $widget + ]; + this.remove_empty_cells.apply(this, rows_to_remove_holes); + } + + return $widget; + }; + + /** + * Move down widgets in cells represented by the arguments col, row, size_x, + * size_y + * + * @method empty_cells + * @param {Number} col The column where the group of cells begin. + * @param {Number} row The row where the group of cells begin. + * @param {Number} size_x The number of columns that the group of cells + * occupy. + * @param {Number} size_y The number of rows that the group of cells + * occupy. + * @param {HTMLElement} $exclude Exclude widgets from being moved. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.empty_cells = function(col, row, size_x, size_y, $exclude) { + var $nexts = this.widgets_below({ + col: col, + row: row - size_y, + size_x: size_x, + size_y: size_y + }); + + $nexts.not($exclude).each($.proxy(function(i, w) { + var wgd = $(w).coords().grid; + if (!(wgd.row <= (row + size_y - 1))) { return; } + var diff = (row + size_y) - wgd.row; + this.move_widget_down($(w), diff); + }, this)); + + this.set_dom_grid_height(); + + return this; + }; + + + /** + * Move up widgets below cells represented by the arguments col, row, size_x, + * size_y. + * + * @method remove_empty_cells + * @param {Number} col The column where the group of cells begin. + * @param {Number} row The row where the group of cells begin. + * @param {Number} size_x The number of columns that the group of cells + * occupy. + * @param {Number} size_y The number of rows that the group of cells + * occupy. + * @param {HTMLElement} $exclude Exclude widgets from being moved. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.remove_empty_cells = function(col, row, size_x, size_y, exclude) { + var $nexts = this.widgets_below({ + col: col, + row: row, + size_x: size_x, + size_y: size_y + }); + + $nexts.not(exclude).each($.proxy(function(i, widget) { + this.move_widget_up( $(widget), size_y ); + }, this)); + + this.set_dom_grid_height(); + + return this; + }; + + + /** + * Get the most left column below to add a new widget. + * + * @method next_position + * @param {Number} size_x The nº of rows the widget occupies horizontally. + * @param {Number} size_y The nº of columns the widget occupies vertically. + * @return {Object} Returns a grid coords object representing the future + * widget coords. + */ + fn.next_position = function(size_x, size_y) { + size_x || (size_x = 1); + size_y || (size_y = 1); + var ga = this.gridmap; + var cols_l = ga.length; + var valid_pos = []; + var rows_l; + + for (var c = 1; c < cols_l; c++) { + rows_l = ga[c].length; + for (var r = 1; r <= rows_l; r++) { + var can_move_to = this.can_move_to({ + size_x: size_x, + size_y: size_y + }, c, r); + + if (can_move_to) { + valid_pos.push({ + col: c, + row: r, + size_y: size_y, + size_x: size_x + }); + } + } + } + + if (valid_pos.length) { + return this.sort_by_row_and_col_asc(valid_pos)[0]; + } + return false; + }; + + + /** + * Remove a widget from the grid. + * + * @method remove_widget + * @param {HTMLElement} el The jQuery wrapped HTMLElement you want to remove. + * @param {Boolean|Function} silent If true, widgets below the removed one + * will not move up. If a Function is passed it will be used as callback. + * @param {Function} callback Function executed when the widget is removed. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.remove_widget = function(el, silent, callback) { + var $el = el instanceof jQuery ? el : $(el); + var wgd = $el.coords().grid; + + // if silent is a function assume it's a callback + if ($.isFunction(silent)) { + callback = silent; + silent = false; + } + + this.cells_occupied_by_placeholder = {}; + this.$widgets = this.$widgets.not($el); + + var $nexts = this.widgets_below($el); + + this.remove_from_gridmap(wgd); + + $el.fadeOut($.proxy(function() { + $el.remove(); + + if (!silent) { + $nexts.each($.proxy(function(i, widget) { + this.move_widget_up( $(widget), wgd.size_y ); + }, this)); + } + + this.set_dom_grid_height(); + + if (callback) { + callback.call(this, el); + } + }, this)); + }; + + + /** + * Remove all widgets from the grid. + * + * @method remove_all_widgets + * @param {Function} callback Function executed for each widget removed. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.remove_all_widgets = function(callback) { + this.$widgets.each($.proxy(function(i, el){ + this.remove_widget(el, true, callback); + }, this)); + + return this; + }; + + + /** + * Returns a serialized array of the widgets in the grid. + * + * @method serialize + * @param {HTMLElement} [$widgets] The collection of jQuery wrapped + * HTMLElements you want to serialize. If no argument is passed all widgets + * will be serialized. + * @return {Array} Returns an Array of Objects with the data specified in + * the serialize_params option. + */ + fn.serialize = function($widgets) { + $widgets || ($widgets = this.$widgets); + var result = []; + $widgets.each($.proxy(function(i, widget) { + result.push(this.options.serialize_params( + $(widget), $(widget).coords().grid ) ); + }, this)); + + return result; + }; + + + /** + * Returns a serialized array of the widgets that have changed their + * position. + * + * @method serialize_changed + * @return {Array} Returns an Array of Objects with the data specified in + * the serialize_params option. + */ + fn.serialize_changed = function() { + return this.serialize(this.$changed); + }; + + + /** + * Creates the grid coords object representing the widget a add it to the + * mapped array of positions. + * + * @method register_widget + * @return {Array} Returns the instance of the Gridster class. + */ + fn.register_widget = function($el) { + + var wgd = { + 'col': parseInt($el.attr('data-col'), 10), + 'row': parseInt($el.attr('data-row'), 10), + 'size_x': parseInt($el.attr('data-sizex'), 10), + 'size_y': parseInt($el.attr('data-sizey'), 10), + 'el': $el + }; + + if (this.options.avoid_overlapped_widgets && + !this.can_move_to( + {size_x: wgd.size_x, size_y: wgd.size_y}, wgd.col, wgd.row) + ) { + wgd = this.next_position(wgd.size_x, wgd.size_y); + wgd.el = $el; + $el.attr({ + 'data-col': wgd.col, + 'data-row': wgd.row, + 'data-sizex': wgd.size_x, + 'data-sizey': wgd.size_y + }); + } + + // attach Coord object to player data-coord attribute + $el.data('coords', $el.coords()); + + // Extend Coord object with grid position info + $el.data('coords').grid = wgd; + + this.add_to_gridmap(wgd, $el); + + return this; + }; + + + /** + * Update in the mapped array of positions the value of cells represented by + * the grid coords object passed in the `grid_data` param. + * + * @param {Object} grid_data The grid coords object representing the cells + * to update in the mapped array. + * @param {HTMLElement|Boolean} value Pass `false` or the jQuery wrapped + * HTMLElement, depends if you want to delete an existing position or add + * a new one. + * @method update_widget_position + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.update_widget_position = function(grid_data, value) { + this.for_each_cell_occupied(grid_data, function(col, row) { + if (!this.gridmap[col]) { return this; } + this.gridmap[col][row] = value; + }); + return this; + }; + + + /** + * Remove a widget from the mapped array of positions. + * + * @method remove_from_gridmap + * @param {Object} grid_data The grid coords object representing the cells + * to update in the mapped array. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.remove_from_gridmap = function(grid_data) { + return this.update_widget_position(grid_data, false); + }; + + + /** + * Add a widget to the mapped array of positions. + * + * @method add_to_gridmap + * @param {Object} grid_data The grid coords object representing the cells + * to update in the mapped array. + * @param {HTMLElement|Boolean} value The value to set in the specified + * position . + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.add_to_gridmap = function(grid_data, value) { + this.update_widget_position(grid_data, value || grid_data.el); + + if (grid_data.el) { + var $widgets = this.widgets_below(grid_data.el); + $widgets.each($.proxy(function(i, widget) { + this.move_widget_up( $(widget)); + }, this)); + } + }; + + + /** + * Make widgets draggable. + * + * @uses Draggable + * @method draggable + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.draggable = function() { + var self = this; + var draggable_options = $.extend(true, {}, this.options.draggable, { + offset_left: this.options.widget_margins[0], + start: function(event, ui) { + self.$widgets.filter('.player-revert') + .removeClass('player-revert'); + + self.$player = $(this); + self.$helper = self.options.draggable.helper === 'clone' ? + $(ui.helper) : self.$player; + self.helper = !self.$helper.is(self.$player); + + self.on_start_drag.call(self, event, ui); + self.$el.trigger('gridster:dragstart'); + }, + stop: function(event, ui) { + self.on_stop_drag.call(self, event, ui); + self.$el.trigger('gridster:dragstop'); + }, + drag: throttle(function(event, ui) { + self.on_drag.call(self, event, ui); + self.$el.trigger('gridster:drag'); + }, 60) + }); + + this.drag_api = this.$el.drag(draggable_options).data('drag'); + return this; + }; + + + /** + * This function is executed when the player begins to be dragged. + * + * @method on_start_drag + * @param {Event} The original browser event + * @param {Object} A prepared ui object. + */ + fn.on_start_drag = function(event, ui) { + + this.$helper.add(this.$player).add(this.$wrapper).addClass('dragging'); + + this.$player.addClass('player'); + this.player_grid_data = this.$player.coords().grid; + this.placeholder_grid_data = $.extend({}, this.player_grid_data); + + //set new grid height along the dragging period + this.$el.css('height', this.$el.height() + + (this.player_grid_data.size_y * this.min_widget_height)); + + var colliders = this.faux_grid; + var coords = this.$player.data('coords').coords; + + this.cells_occupied_by_player = this.get_cells_occupied( + this.player_grid_data); + this.cells_occupied_by_placeholder = this.get_cells_occupied( + this.placeholder_grid_data); + + this.last_cols = []; + this.last_rows = []; + + + // see jquery.collision.js + this.collision_api = this.$helper.collision( + colliders, this.options.collision); + + this.$preview_holder = $('
    • ', { + 'class': 'preview-holder', + 'data-row': this.$player.attr('data-row'), + 'data-col': this.$player.attr('data-col'), + css: { + width: coords.width, + height: coords.height + } + }).appendTo(this.$el); + + if (this.options.draggable.start) { + this.options.draggable.start.call(this, event, ui); + } + }; + + + /** + * This function is executed when the player is being dragged. + * + * @method on_drag + * @param {Event} The original browser event + * @param {Object} A prepared ui object. + */ + fn.on_drag = function(event, ui) { + //break if dragstop has been fired + if (this.$player === null) { + return false; + } + + var abs_offset = { + left: ui.position.left + this.baseX, + top: ui.position.top + this.baseY + }; + + this.colliders_data = this.collision_api.get_closest_colliders( + abs_offset); + + this.on_overlapped_column_change( + this.on_start_overlapping_column, + this.on_stop_overlapping_column + ); + + this.on_overlapped_row_change( + this.on_start_overlapping_row, + this.on_stop_overlapping_row + ); + + if (this.helper && this.$player) { + this.$player.css({ + 'left': ui.position.left, + 'top': ui.position.top + }); + } + + if (this.options.draggable.drag) { + this.options.draggable.drag.call(this, event, ui); + } + }; + + /** + * This function is executed when the player stops being dragged. + * + * @method on_stop_drag + * @param {Event} The original browser event + * @param {Object} A prepared ui object. + */ + fn.on_stop_drag = function(event, ui) { + this.$helper.add(this.$player).add(this.$wrapper) + .removeClass('dragging'); + + ui.position.left = ui.position.left + this.baseX; + ui.position.top = ui.position.top + this.baseY; + this.colliders_data = this.collision_api.get_closest_colliders(ui.position); + + this.on_overlapped_column_change( + this.on_start_overlapping_column, + this.on_stop_overlapping_column + ); + + this.on_overlapped_row_change( + this.on_start_overlapping_row, + this.on_stop_overlapping_row + ); + + this.$player.addClass('player-revert').removeClass('player') + .attr({ + 'data-col': this.placeholder_grid_data.col, + 'data-row': this.placeholder_grid_data.row + }).css({ + 'left': '', + 'top': '' + }); + + this.$changed = this.$changed.add(this.$player); + + this.cells_occupied_by_player = this.get_cells_occupied( + this.placeholder_grid_data); + this.set_cells_player_occupies( + this.placeholder_grid_data.col, this.placeholder_grid_data.row); + + this.$player.coords().grid.row = this.placeholder_grid_data.row; + this.$player.coords().grid.col = this.placeholder_grid_data.col; + + if (this.options.draggable.stop) { + this.options.draggable.stop.call(this, event, ui); + } + + this.$preview_holder.remove(); + + this.$player = null; + this.$helper = null; + this.placeholder_grid_data = {}; + this.player_grid_data = {}; + this.cells_occupied_by_placeholder = {}; + this.cells_occupied_by_player = {}; + + this.set_dom_grid_height(); + }; + + + /** + * Executes the callbacks passed as arguments when a column begins to be + * overlapped or stops being overlapped. + * + * @param {Function} start_callback Function executed when a new column + * begins to be overlapped. The column is passed as first argument. + * @param {Function} stop_callback Function executed when a column stops + * being overlapped. The column is passed as first argument. + * @method on_overlapped_column_change + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.on_overlapped_column_change = function(start_callback, stop_callback) { + if (!this.colliders_data.length) { + return; + } + var cols = this.get_targeted_columns( + this.colliders_data[0].el.data.col); + + var last_n_cols = this.last_cols.length; + var n_cols = cols.length; + var i; + + for (i = 0; i < n_cols; i++) { + if ($.inArray(cols[i], this.last_cols) === -1) { + (start_callback || $.noop).call(this, cols[i]); + } + } + + for (i = 0; i< last_n_cols; i++) { + if ($.inArray(this.last_cols[i], cols) === -1) { + (stop_callback || $.noop).call(this, this.last_cols[i]); + } + } + + this.last_cols = cols; + + return this; + }; + + + /** + * Executes the callbacks passed as arguments when a row starts to be + * overlapped or stops being overlapped. + * + * @param {Function} start_callback Function executed when a new row begins + * to be overlapped. The row is passed as first argument. + * @param {Function} stop_callback Function executed when a row stops being + * overlapped. The row is passed as first argument. + * @method on_overlapped_row_change + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.on_overlapped_row_change = function(start_callback, end_callback) { + if (!this.colliders_data.length) { + return; + } + var rows = this.get_targeted_rows(this.colliders_data[0].el.data.row); + var last_n_rows = this.last_rows.length; + var n_rows = rows.length; + var i; + + for (i = 0; i < n_rows; i++) { + if ($.inArray(rows[i], this.last_rows) === -1) { + (start_callback || $.noop).call(this, rows[i]); + } + } + + for (i = 0; i < last_n_rows; i++) { + if ($.inArray(this.last_rows[i], rows) === -1) { + (end_callback || $.noop).call(this, this.last_rows[i]); + } + } + + this.last_rows = rows; + }; + + + /** + * Sets the current position of the player + * + * @param {Function} start_callback Function executed when a new row begins + * to be overlapped. The row is passed as first argument. + * @param {Function} stop_callback Function executed when a row stops being + * overlapped. The row is passed as first argument. + * @method set_player + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.set_player = function(col, row, no_player) { + var self = this; + if (!no_player) { + this.empty_cells_player_occupies(); + } + var cell = !no_player ? self.colliders_data[0].el.data : {col: col}; + var to_col = cell.col; + var to_row = row || cell.row; + + this.player_grid_data = { + col: to_col, + row: to_row, + size_y : this.player_grid_data.size_y, + size_x : this.player_grid_data.size_x + }; + + this.cells_occupied_by_player = this.get_cells_occupied( + this.player_grid_data); + + var $overlapped_widgets = this.get_widgets_overlapped( + this.player_grid_data); + + var constraints = this.widgets_constraints($overlapped_widgets); + + this.manage_movements(constraints.can_go_up, to_col, to_row); + this.manage_movements(constraints.can_not_go_up, to_col, to_row); + + /* if there is not widgets overlapping in the new player position, + * update the new placeholder position. */ + if (!$overlapped_widgets.length) { + var pp = this.can_go_player_up(this.player_grid_data); + if (pp !== false) { + to_row = pp; + } + this.set_placeholder(to_col, to_row); + } + + return { + col: to_col, + row: to_row + }; + }; + + + /** + * See which of the widgets in the $widgets param collection can go to + * a upper row and which not. + * + * @method widgets_contraints + * @param {HTMLElements} $widgets A jQuery wrapped collection of + * HTMLElements. + * @return {Array} Returns a literal Object with two keys: `can_go_up` & + * `can_not_go_up`. Each contains a set of HTMLElements. + */ + fn.widgets_constraints = function($widgets) { + var $widgets_can_go_up = $([]); + var $widgets_can_not_go_up; + var wgd_can_go_up = []; + var wgd_can_not_go_up = []; + + $widgets.each($.proxy(function(i, w) { + var $w = $(w); + var wgd = $w.coords().grid; + if (this.can_go_widget_up(wgd)) { + $widgets_can_go_up = $widgets_can_go_up.add($w); + wgd_can_go_up.push(wgd); + }else{ + wgd_can_not_go_up.push(wgd); + } + }, this)); + + $widgets_can_not_go_up = $widgets.not($widgets_can_go_up); + + return { + can_go_up: this.sort_by_row_asc(wgd_can_go_up), + can_not_go_up: this.sort_by_row_desc(wgd_can_not_go_up) + }; + }; + + + /** + * Sorts an Array of grid coords objects (representing the grid coords of + * each widget) in ascending way. + * + * @method sort_by_row_asc + * @param {Array} widgets Array of grid coords objects + * @return {Array} Returns the array sorted. + */ + fn.sort_by_row_asc = function(widgets) { + widgets = widgets.sort(function(a, b) { + if (!a.row) { + a = $(a).coords().grid; + b = $(b).coords().grid; + } + + if (a.row > b.row) { + return 1; + } + return -1; + }); + + return widgets; + }; + + + /** + * Sorts an Array of grid coords objects (representing the grid coords of + * each widget) placing first the empty cells upper left. + * + * @method sort_by_row_and_col_asc + * @param {Array} widgets Array of grid coords objects + * @return {Array} Returns the array sorted. + */ + fn.sort_by_row_and_col_asc = function(widgets) { + widgets = widgets.sort(function(a, b) { + if (a.row > b.row || a.row === b.row && a.col > b.col) { + return 1; + } + return -1; + }); + + return widgets; + }; + + + /** + * Sorts an Array of grid coords objects by column (representing the grid + * coords of each widget) in ascending way. + * + * @method sort_by_col_asc + * @param {Array} widgets Array of grid coords objects + * @return {Array} Returns the array sorted. + */ + fn.sort_by_col_asc = function(widgets) { + widgets = widgets.sort(function(a, b) { + if (a.col > b.col) { + return 1; + } + return -1; + }); + + return widgets; + }; + + + /** + * Sorts an Array of grid coords objects (representing the grid coords of + * each widget) in descending way. + * + * @method sort_by_row_desc + * @param {Array} widgets Array of grid coords objects + * @return {Array} Returns the array sorted. + */ + fn.sort_by_row_desc = function(widgets) { + widgets = widgets.sort(function(a, b) { + if (a.row + a.size_y < b.row + b.size_y) { + return 1; + } + return -1; + }); + return widgets; + }; + + + /** + * Sorts an Array of grid coords objects (representing the grid coords of + * each widget) in descending way. + * + * @method manage_movements + * @param {HTMLElements} $widgets A jQuery collection of HTMLElements + * representing the widgets you want to move. + * @param {Number} to_col The column to which we want to move the widgets. + * @param {Number} to_row The row to which we want to move the widgets. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.manage_movements = function($widgets, to_col, to_row) { + $.each($widgets, $.proxy(function(i, w) { + var wgd = w; + var $w = wgd.el; + + var can_go_widget_up = this.can_go_widget_up(wgd); + + if (can_go_widget_up) { + //target CAN go up + //so move widget up + this.move_widget_to($w, can_go_widget_up); + this.set_placeholder(to_col, can_go_widget_up + wgd.size_y); + + } else { + //target can't go up + var can_go_player_up = this.can_go_player_up( + this.player_grid_data); + + if (!can_go_player_up) { + // target can't go up + // player cant't go up + // so we need to move widget down to a position that dont + // overlaps player + var y = (to_row + this.player_grid_data.size_y) - wgd.row; + + this.move_widget_down($w, y); + this.set_placeholder(to_col, to_row); + } + } + }, this)); + + return this; + }; + + /** + * Determines if there is a widget in the row and col given. Or if the + * HTMLElement passed as first argument is the player. + * + * @method is_player + * @param {Number|HTMLElement} col_or_el A jQuery wrapped collection of + * HTMLElements. + * @param {Number} [row] The column to which we want to move the widgets. + * @return {Boolean} Returns true or false. + */ + fn.is_player = function(col_or_el, row) { + if (row && !this.gridmap[col_or_el]) { return false; } + var $w = row ? this.gridmap[col_or_el][row] : col_or_el; + return $w && ($w.is(this.$player) || $w.is(this.$helper)); + }; + + + /** + * Determines if the widget that is being dragged is currently over the row + * and col given. + * + * @method is_player_in + * @param {Number} col The column to check. + * @param {Number} row The row to check. + * @return {Boolean} Returns true or false. + */ + fn.is_player_in = function(col, row) { + var c = this.cells_occupied_by_player || {}; + return $.inArray(col, c.cols) >= 0 && $.inArray(row, c.rows) >= 0; + }; + + + /** + * Determines if the placeholder is currently over the row and col given. + * + * @method is_placeholder_in + * @param {Number} col The column to check. + * @param {Number} row The row to check. + * @return {Boolean} Returns true or false. + */ + fn.is_placeholder_in = function(col, row) { + var c = this.cells_occupied_by_placeholder || {}; + return this.is_placeholder_in_col(col) && $.inArray(row, c.rows) >= 0; + }; + + + /** + * Determines if the placeholder is currently over the column given. + * + * @method is_placeholder_in_col + * @param {Number} col The column to check. + * @return {Boolean} Returns true or false. + */ + fn.is_placeholder_in_col = function(col) { + var c = this.cells_occupied_by_placeholder || []; + return $.inArray(col, c.cols) >= 0; + }; + + + /** + * Determines if the cell represented by col and row params is empty. + * + * @method is_empty + * @param {Number} col The column to check. + * @param {Number} row The row to check. + * @return {Boolean} Returns true or false. + */ + fn.is_empty = function(col, row) { + if (typeof this.gridmap[col] !== 'undefined' && + typeof this.gridmap[col][row] !== 'undefined' && + this.gridmap[col][row] === false + ) { + return true; + } + return false; + }; + + + /** + * Determines if the cell represented by col and row params is occupied. + * + * @method is_occupied + * @param {Number} col The column to check. + * @param {Number} row The row to check. + * @return {Boolean} Returns true or false. + */ + fn.is_occupied = function(col, row) { + if (!this.gridmap[col]) { + return false; + } + + if (this.gridmap[col][row]) { + return true; + } + return false; + }; + + + /** + * Determines if there is a widget in the cell represented by col/row params. + * + * @method is_widget + * @param {Number} col The column to check. + * @param {Number} row The row to check. + * @return {Boolean|HTMLElement} Returns false if there is no widget, + * else returns the jQuery HTMLElement + */ + fn.is_widget = function(col, row) { + var cell = this.gridmap[col]; + if (!cell) { + return false; + } + + cell = cell[row]; + + if (cell) { + return cell; + } + + return false; + }; + + + /** + * Determines if there is a widget in the cell represented by col/row + * params and if this is under the widget that is being dragged. + * + * @method is_widget_under_player + * @param {Number} col The column to check. + * @param {Number} row The row to check. + * @return {Boolean} Returns true or false. + */ + fn.is_widget_under_player = function(col, row) { + if (this.is_widget(col, row)) { + return this.is_player_in(col, row); + } + return false; + }; + + + /** + * Get widgets overlapping with the player or with the object passed + * representing the grid cells. + * + * @method get_widgets_under_player + * @return {HTMLElement} Returns a jQuery collection of HTMLElements + */ + fn.get_widgets_under_player = function(cells) { + cells || (cells = this.cells_occupied_by_player || {cols: [], rows: []}); + var $widgets = $([]); + + $.each(cells.cols, $.proxy(function(i, col) { + $.each(cells.rows, $.proxy(function(i, row) { + if(this.is_widget(col, row)) { + $widgets = $widgets.add(this.gridmap[col][row]); + } + }, this)); + }, this)); + + return $widgets; + }; + + + /** + * Put placeholder at the row and column specified. + * + * @method set_placeholder + * @param {Number} col The column to which we want to move the + * placeholder. + * @param {Number} row The row to which we want to move the + * placeholder. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.set_placeholder = function(col, row) { + var phgd = $.extend({}, this.placeholder_grid_data); + var $nexts = this.widgets_below({ + col: phgd.col, + row: phgd.row, + size_y: phgd.size_y, + size_x: phgd.size_x + }); + + // Prevents widgets go out of the grid + var right_col = (col + phgd.size_x - 1); + if (right_col > this.cols) { + col = col - (right_col - col); + } + + var moved_down = this.placeholder_grid_data.row < row; + var changed_column = this.placeholder_grid_data.col !== col; + + this.placeholder_grid_data.col = col; + this.placeholder_grid_data.row = row; + + this.cells_occupied_by_placeholder = this.get_cells_occupied( + this.placeholder_grid_data); + + this.$preview_holder.attr({ + 'data-row' : row, + 'data-col' : col + }); + + if (moved_down || changed_column) { + $nexts.each($.proxy(function(i, widget) { + this.move_widget_up( + $(widget), this.placeholder_grid_data.col - col + phgd.size_y); + }, this)); + } + + + var $widgets_under_ph = this.get_widgets_under_player(this.cells_occupied_by_placeholder); + if ($widgets_under_ph.length) { + $widgets_under_ph.each($.proxy(function(i, widget) { + var $w = $(widget); + this.move_widget_down( + $w, row + phgd.size_y - $w.data('coords').grid.row); + }, this)); + } + + }; + + + /** + * Determines whether the player can move to a position above. + * + * @method can_go_player_up + * @param {Object} widget_grid_data The actual grid coords object of the + * player. + * @return {Number|Boolean} If the player can be moved to an upper row + * returns the row number, else returns false. + */ + fn.can_go_player_up = function(widget_grid_data) { + var p_bottom_row = widget_grid_data.row + widget_grid_data.size_y - 1; + var result = true; + var upper_rows = []; + var min_row = 10000; + var $widgets_under_player = this.get_widgets_under_player(); + + /* generate an array with columns as index and array with upper rows + * empty as value */ + this.for_each_column_occupied(widget_grid_data, function(tcol) { + var grid_col = this.gridmap[tcol]; + var r = p_bottom_row + 1; + upper_rows[tcol] = []; + + while (--r > 0) { + if (this.is_empty(tcol, r) || this.is_player(tcol, r) || + this.is_widget(tcol, r) && + grid_col[r].is($widgets_under_player) + ) { + upper_rows[tcol].push(r); + min_row = r < min_row ? r : min_row; + }else{ + break; + } + } + + if (upper_rows[tcol].length === 0) { + result = false; + return true; //break + } + + upper_rows[tcol].sort(); + }); + + if (!result) { return false; } + + return this.get_valid_rows(widget_grid_data, upper_rows, min_row); + }; + + + /** + * Determines whether a widget can move to a position above. + * + * @method can_go_widget_up + * @param {Object} widget_grid_data The actual grid coords object of the + * widget we want to check. + * @return {Number|Boolean} If the widget can be moved to an upper row + * returns the row number, else returns false. + */ + fn.can_go_widget_up = function(widget_grid_data) { + var p_bottom_row = widget_grid_data.row + widget_grid_data.size_y - 1; + var result = true; + var upper_rows = []; + var min_row = 10000; + + /* generate an array with columns as index and array with topmost rows + * empty as value */ + this.for_each_column_occupied(widget_grid_data, function(tcol) { + var grid_col = this.gridmap[tcol]; + upper_rows[tcol] = []; + + var r = p_bottom_row + 1; + // iterate over each row + while (--r > 0) { + if (this.is_widget(tcol, r) && !this.is_player_in(tcol, r)) { + if (!grid_col[r].is(widget_grid_data.el)) { + break; + } + } + + if (!this.is_player(tcol, r) && + !this.is_placeholder_in(tcol, r) && + !this.is_player_in(tcol, r)) { + upper_rows[tcol].push(r); + } + + if (r < min_row) { + min_row = r; + } + } + + if (upper_rows[tcol].length === 0) { + result = false; + return true; //break + } + + upper_rows[tcol].sort(); + }); + + if (!result) { return false; } + + return this.get_valid_rows(widget_grid_data, upper_rows, min_row); + }; + + + /** + * Search a valid row for the widget represented by `widget_grid_data' in + * the `upper_rows` array. Iteration starts from row specified in `min_row`. + * + * @method get_valid_rows + * @param {Object} widget_grid_data The actual grid coords object of the + * player. + * @param {Array} upper_rows An array with columns as index and arrays + * of valid rows as values. + * @param {Number} min_row The upper row from which the iteration will start. + * @return {Number|Boolean} Returns the upper row valid from the `upper_rows` + * for the widget in question. + */ + fn.get_valid_rows = function(widget_grid_data, upper_rows, min_row) { + var p_top_row = widget_grid_data.row; + var p_bottom_row = widget_grid_data.row + widget_grid_data.size_y - 1; + var size_y = widget_grid_data.size_y; + var r = min_row - 1; + var valid_rows = []; + + while (++r <= p_bottom_row ) { + var common = true; + $.each(upper_rows, function(col, rows) { + if ($.isArray(rows) && $.inArray(r, rows) === -1) { + common = false; + } + }); + + if (common === true) { + valid_rows.push(r); + if (valid_rows.length === size_y) { + break; + } + } + } + + var new_row = false; + if (size_y === 1) { + if (valid_rows[0] !== p_top_row) { + new_row = valid_rows[0] || false; + } + }else{ + if (valid_rows[0] !== p_top_row) { + new_row = this.get_consecutive_numbers_index( + valid_rows, size_y); + } + } + + return new_row; + }; + + + fn.get_consecutive_numbers_index = function(arr, size_y) { + var max = arr.length; + var result = []; + var first = true; + var prev = -1; // or null? + + for (var i=0; i < max; i++) { + if (first || arr[i] === prev + 1) { + result.push(i); + if (result.length === size_y) { + break; + } + first = false; + }else{ + result = []; + first = true; + } + + prev = arr[i]; + } + + return result.length >= size_y ? arr[result[0]] : false; + }; + + + /** + * Get widgets overlapping with the player. + * + * @method get_widgets_overlapped + * @return {HTMLElements} Returns a jQuery collection of HTMLElements. + */ + fn.get_widgets_overlapped = function() { + var $w; + var $widgets = $([]); + var used = []; + var rows_from_bottom = this.cells_occupied_by_player.rows.slice(0); + rows_from_bottom.reverse(); + + $.each(this.cells_occupied_by_player.cols, $.proxy(function(i, col) { + $.each(rows_from_bottom, $.proxy(function(i, row) { + // if there is a widget in the player position + if (!this.gridmap[col]) { return true; } //next iteration + var $w = this.gridmap[col][row]; + if (this.is_occupied(col, row) && !this.is_player($w) && + $.inArray($w, used) === -1 + ) { + $widgets = $widgets.add($w); + used.push($w); + } + + }, this)); + }, this)); + + return $widgets; + }; + + + /** + * This callback is executed when the player begins to collide with a column. + * + * @method on_start_overlapping_column + * @param {Number} col The collided column. + * @return {HTMLElements} Returns a jQuery collection of HTMLElements. + */ + fn.on_start_overlapping_column = function(col) { + this.set_player(col, false); + }; + + + /** + * A callback executed when the player begins to collide with a row. + * + * @method on_start_overlapping_row + * @param {Number} col The collided row. + * @return {HTMLElements} Returns a jQuery collection of HTMLElements. + */ + fn.on_start_overlapping_row = function(row) { + this.set_player(false, row); + }; + + + /** + * A callback executed when the the player ends to collide with a column. + * + * @method on_stop_overlapping_column + * @param {Number} col The collided row. + * @return {HTMLElements} Returns a jQuery collection of HTMLElements. + */ + fn.on_stop_overlapping_column = function(col) { + this.set_player(col, false); + + var self = this; + this.for_each_widget_below(col, this.cells_occupied_by_player.rows[0], + function(tcol, trow) { + self.move_widget_up(this, self.player_grid_data.size_y); + }); + }; + + + /** + * This callback is executed when the player ends to collide with a row. + * + * @method on_stop_overlapping_row + * @param {Number} row The collided row. + * @return {HTMLElements} Returns a jQuery collection of HTMLElements. + */ + fn.on_stop_overlapping_row = function(row) { + this.set_player(false, row); + + var self = this; + var cols = this.cells_occupied_by_player.cols; + for (var c = 0, cl = cols.length; c < cl; c++) { + this.for_each_widget_below(cols[c], row, function(tcol, trow) { + self.move_widget_up(this, self.player_grid_data.size_y); + }); + } + }; + + + /** + * Move a widget to a specific row. The cell or cells must be empty. + * If the widget has widgets below, all of these widgets will be moved also + * if they can. + * + * @method move_widget_to + * @param {HTMLElement} $widget The jQuery wrapped HTMLElement of the + * widget is going to be moved. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.move_widget_to = function($widget, row) { + var self = this; + var widget_grid_data = $widget.coords().grid; + var diff = row - widget_grid_data.row; + var $next_widgets = this.widgets_below($widget); + + var can_move_to_new_cell = this.can_move_to( + widget_grid_data, widget_grid_data.col, row, $widget); + + if (can_move_to_new_cell === false) { + return false; + } + + this.remove_from_gridmap(widget_grid_data); + widget_grid_data.row = row; + this.add_to_gridmap(widget_grid_data); + $widget.attr('data-row', row); + this.$changed = this.$changed.add($widget); + + + $next_widgets.each(function(i, widget) { + var $w = $(widget); + var wgd = $w.coords().grid; + var can_go_up = self.can_go_widget_up(wgd); + if (can_go_up && can_go_up !== wgd.row) { + self.move_widget_to($w, can_go_up); + } + }); + + return this; + }; + + + /** + * Move up the specified widget and all below it. + * + * @method move_widget_up + * @param {HTMLElement} $widget The widget you want to move. + * @param {Number} [y_units] The number of cells that the widget has to move. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.move_widget_up = function($widget, y_units) { + var el_grid_data = $widget.coords().grid; + var actual_row = el_grid_data.row; + var moved = []; + var can_go_up = true; + y_units || (y_units = 1); + + if (!this.can_go_up($widget)) { return false; } //break; + + this.for_each_column_occupied(el_grid_data, function(col) { + // can_go_up + if ($.inArray($widget, moved) === -1) { + var widget_grid_data = $widget.coords().grid; + var next_row = actual_row - y_units; + next_row = this.can_go_up_to_row( + widget_grid_data, col, next_row); + + if (!next_row) { + return true; + } + + var $next_widgets = this.widgets_below($widget); + + this.remove_from_gridmap(widget_grid_data); + widget_grid_data.row = next_row; + this.add_to_gridmap(widget_grid_data); + $widget.attr('data-row', widget_grid_data.row); + this.$changed = this.$changed.add($widget); + + moved.push($widget); + + $next_widgets.each($.proxy(function(i, widget) { + this.move_widget_up($(widget), y_units); + }, this)); + } + }); + + }; + + + /** + * Move down the specified widget and all below it. + * + * @method move_widget_down + * @param {HTMLElement} $widget The jQuery object representing the widget + * you want to move. + * @param {Number} The number of cells that the widget has to move. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.move_widget_down = function($widget, y_units) { + var el_grid_data = $widget.coords().grid; + var actual_row = el_grid_data.row; + var moved = []; + var y_diff = y_units; + + if (!$widget) { return false; } + + if ($.inArray($widget, moved) === -1) { + + var widget_grid_data = $widget.coords().grid; + var next_row = actual_row + y_units; + var $next_widgets = this.widgets_below($widget); + + this.remove_from_gridmap(widget_grid_data); + + $next_widgets.each($.proxy(function(i, widget) { + var $w = $(widget); + var wd = $w.coords().grid; + var tmp_y = this.displacement_diff( + wd, widget_grid_data, y_diff); + + if (tmp_y > 0) { + this.move_widget_down($w, tmp_y); + } + }, this)); + + widget_grid_data.row = next_row; + this.update_widget_position(widget_grid_data, $widget); + $widget.attr('data-row', widget_grid_data.row); + this.$changed = this.$changed.add($widget); + + moved.push($widget); + } + }; + + + /** + * Check if the widget can move to the specified row, else returns the + * upper row possible. + * + * @method can_go_up_to_row + * @param {Number} widget_grid_data The current grid coords object of the + * widget. + * @param {Number} col The target column. + * @param {Number} row The target row. + * @return {Boolean|Number} Returns the row number if the widget can move + * to the target position, else returns false. + */ + fn.can_go_up_to_row = function(widget_grid_data, col, row) { + var ga = this.gridmap; + var result = true; + var urc = []; // upper_rows_in_columns + var actual_row = widget_grid_data.row; + var r; + + /* generate an array with columns as index and array with + * upper rows empty in the column */ + this.for_each_column_occupied(widget_grid_data, function(tcol) { + var grid_col = ga[tcol]; + urc[tcol] = []; + + r = actual_row; + while (r--) { + if (this.is_empty(tcol, r) && + !this.is_placeholder_in(tcol, r) + ) { + urc[tcol].push(r); + }else{ + break; + } + } + + if (!urc[tcol].length) { + result = false; + return true; + } + + }); + + if (!result) { return false; } + + /* get common rows starting from upper position in all the columns + * that widget occupies */ + r = row; + for (r = 1; r < actual_row; r++) { + var common = true; + + for (var uc = 0, ucl = urc.length; uc < ucl; uc++) { + if (urc[uc] && $.inArray(r, urc[uc]) === -1) { + common = false; + } + } + + if (common === true) { + result = r; + break; + } + } + + return result; + }; + + + fn.displacement_diff = function(widget_grid_data, parent_bgd, y_units) { + var actual_row = widget_grid_data.row; + var diffs = []; + var parent_max_y = parent_bgd.row + parent_bgd.size_y; + + this.for_each_column_occupied(widget_grid_data, function(col) { + var temp_y_units = 0; + + for (var r = parent_max_y; r < actual_row; r++) { + if (this.is_empty(col, r)) { + temp_y_units = temp_y_units + 1; + } + } + + diffs.push(temp_y_units); + }); + + var max_diff = Math.max.apply(Math, diffs); + y_units = (y_units - max_diff); + + return y_units > 0 ? y_units : 0; + }; + + + /** + * Get widgets below a widget. + * + * @method widgets_below + * @param {HTMLElement} $el The jQuery wrapped HTMLElement. + * @return {HTMLElements} A jQuery collection of HTMLElements. + */ + fn.widgets_below = function($el) { + var el_grid_data = $.isPlainObject($el) ? $el : $el.coords().grid; + var self = this; + var ga = this.gridmap; + var next_row = el_grid_data.row + el_grid_data.size_y - 1; + var $nexts = $([]); + + this.for_each_column_occupied(el_grid_data, function(col) { + self.for_each_widget_below(col, next_row, function(tcol, trow) { + if (!self.is_player(this) && $.inArray(this, $nexts) === -1) { + $nexts = $nexts.add(this); + return true; // break + } + }); + }); + + return this.sort_by_row_asc($nexts); + }; + + + /** + * Update the array of mapped positions with the new player position. + * + * @method set_cells_player_occupies + * @param {Number} col The new player col. + * @param {Number} col The new player row. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.set_cells_player_occupies = function(col, row) { + this.remove_from_gridmap(this.placeholder_grid_data); + this.placeholder_grid_data.col = col; + this.placeholder_grid_data.row = row; + this.add_to_gridmap(this.placeholder_grid_data, this.$player); + return this; + }; + + + /** + * Remove from the array of mapped positions the reference to the player. + * + * @method empty_cells_player_occupies + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.empty_cells_player_occupies = function() { + this.remove_from_gridmap(this.placeholder_grid_data); + return this; + }; + + + fn.can_go_up = function($el) { + var el_grid_data = $el.coords().grid; + var initial_row = el_grid_data.row; + var prev_row = initial_row - 1; + var ga = this.gridmap; + var upper_rows_by_column = []; + + var result = true; + if (initial_row === 1) { return false; } + + this.for_each_column_occupied(el_grid_data, function(col) { + var $w = this.is_widget(col, prev_row); + + if (this.is_occupied(col, prev_row) || + this.is_player(col, prev_row) || + this.is_placeholder_in(col, prev_row) || + this.is_player_in(col, prev_row) + ) { + result = false; + return true; //break + } + }); + + return result; + }; + + + + /** + * Check if it's possible to move a widget to a specific col/row. It takes + * into account the dimensions (`size_y` and `size_x` attrs. of the grid + * coords object) the widget occupies. + * + * @method can_move_to + * @param {Object} widget_grid_data The grid coords object that represents + * the widget. + * @param {Object} col The col to check. + * @param {Object} row The row to check. + * @param {Number} [max_row] The max row allowed. + * @return {Boolean} Returns true if all cells are empty, else return false. + */ + fn.can_move_to = function(widget_grid_data, col, row, max_row) { + var ga = this.gridmap; + var $w = widget_grid_data.el; + var future_wd = { + size_y: widget_grid_data.size_y, + size_x: widget_grid_data.size_x, + col: col, + row: row + }; + var result = true; + + //Prevents widgets go out of the grid + var right_col = col + widget_grid_data.size_x - 1; + if (right_col > this.cols) { + return false; + } + + if (max_row && max_row < row + widget_grid_data.size_y - 1) { + return false; + } + + this.for_each_cell_occupied(future_wd, function(tcol, trow) { + var $tw = this.is_widget(tcol, trow); + if ($tw && (!widget_grid_data.el || $tw.is($w))) { + result = false; + } + }); + + return result; + }; + + + /** + * Given the leftmost column returns all columns that are overlapping + * with the player. + * + * @method get_targeted_columns + * @param {Number} [from_col] The leftmost column. + * @return {Array} Returns an array with column numbers. + */ + fn.get_targeted_columns = function(from_col) { + var max = (from_col || this.player_grid_data.col) + + (this.player_grid_data.size_x - 1); + var cols = []; + for (var col = from_col; col <= max; col++) { + cols.push(col); + } + return cols; + }; + + + /** + * Given the upper row returns all rows that are overlapping with the player. + * + * @method get_targeted_rows + * @param {Number} [from_row] The upper row. + * @return {Array} Returns an array with row numbers. + */ + fn.get_targeted_rows = function(from_row) { + var max = (from_row || this.player_grid_data.row) + + (this.player_grid_data.size_y - 1); + var rows = []; + for (var row = from_row; row <= max; row++) { + rows.push(row); + } + return rows; + }; + + /** + * Get all columns and rows that a widget occupies. + * + * @method get_cells_occupied + * @param {Object} el_grid_data The grid coords object of the widget. + * @return {Object} Returns an object like `{ cols: [], rows: []}`. + */ + fn.get_cells_occupied = function(el_grid_data) { + var cells = { cols: [], rows: []}; + var i; + if (arguments[1] instanceof jQuery) { + el_grid_data = arguments[1].coords().grid; + } + + for (i = 0; i < el_grid_data.size_x; i++) { + var col = el_grid_data.col + i; + cells.cols.push(col); + } + + for (i = 0; i < el_grid_data.size_y; i++) { + var row = el_grid_data.row + i; + cells.rows.push(row); + } + + return cells; + }; + + + /** + * Iterate over the cells occupied by a widget executing a function for + * each one. + * + * @method for_each_cell_occupied + * @param {Object} el_grid_data The grid coords object that represents the + * widget. + * @param {Function} callback The function to execute on each column + * iteration. Column and row are passed as arguments. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.for_each_cell_occupied = function(grid_data, callback) { + this.for_each_column_occupied(grid_data, function(col) { + this.for_each_row_occupied(grid_data, function(row) { + callback.call(this, col, row); + }); + }); + return this; + }; + + + /** + * Iterate over the columns occupied by a widget executing a function for + * each one. + * + * @method for_each_column_occupied + * @param {Object} el_grid_data The grid coords object that represents + * the widget. + * @param {Function} callback The function to execute on each column + * iteration. The column number is passed as first argument. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.for_each_column_occupied = function(el_grid_data, callback) { + for (var i = 0; i < el_grid_data.size_x; i++) { + var col = el_grid_data.col + i; + callback.call(this, col, el_grid_data); + } + }; + + + /** + * Iterate over the rows occupied by a widget executing a function for + * each one. + * + * @method for_each_row_occupied + * @param {Object} el_grid_data The grid coords object that represents + * the widget. + * @param {Function} callback The function to execute on each column + * iteration. The row number is passed as first argument. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.for_each_row_occupied = function(el_grid_data, callback) { + for (var i = 0; i < el_grid_data.size_y; i++) { + var row = el_grid_data.row + i; + callback.call(this, row, el_grid_data); + } + }; + + + + fn._traversing_widgets = function(type, direction, col, row, callback) { + var ga = this.gridmap; + if (!ga[col]) { return; } + + var cr, max; + var action = type + '/' + direction; + if (arguments[2] instanceof jQuery) { + var el_grid_data = arguments[2].coords().grid; + col = el_grid_data.col; + row = el_grid_data.row; + callback = arguments[3]; + } + var matched = []; + var trow = row; + + + var methods = { + 'for_each/above': function() { + while (trow--) { + if (trow > 0 && this.is_widget(col, trow) && + $.inArray(ga[col][trow], matched) === -1 + ) { + cr = callback.call(ga[col][trow], col, trow); + matched.push(ga[col][trow]); + if (cr) { break; } + } + } + }, + 'for_each/below': function() { + for (trow = row + 1, max = ga[col].length; trow < max; trow++) { + if (this.is_widget(col, trow) && + $.inArray(ga[col][trow], matched) === -1 + ) { + cr = callback.call(ga[col][trow], col, trow); + matched.push(ga[col][trow]); + if (cr) { break; } + } + } + } + }; + + if (methods[action]) { + methods[action].call(this); + } + }; + + + /** + * Iterate over each widget above the column and row specified. + * + * @method for_each_widget_above + * @param {Number} col The column to start iterating. + * @param {Number} row The row to start iterating. + * @param {Function} callback The function to execute on each widget + * iteration. The value of `this` inside the function is the jQuery + * wrapped HTMLElement. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.for_each_widget_above = function(col, row, callback) { + this._traversing_widgets('for_each', 'above', col, row, callback); + return this; + }; + + + /** + * Iterate over each widget below the column and row specified. + * + * @method for_each_widget_below + * @param {Number} col The column to start iterating. + * @param {Number} row The row to start iterating. + * @param {Function} callback The function to execute on each widget + * iteration. The value of `this` inside the function is the jQuery wrapped + * HTMLElement. + * @return {Class} Returns the instance of the Gridster Class. + */ + fn.for_each_widget_below = function(col, row, callback) { + this._traversing_widgets('for_each', 'below', col, row, callback); + return this; + }; + + + /** + * Returns the highest occupied cell in the grid. + * + * @method get_highest_occupied_cell + * @return {Object} Returns an object with `col` and `row` numbers. + */ + fn.get_highest_occupied_cell = function() { + var r; + var gm = this.gridmap; + var rows = []; + var row_in_col = []; + for (var c = gm.length - 1; c >= 1; c--) { + for (r = gm[c].length - 1; r >= 1; r--) { + if (this.is_widget(c, r)) { + rows.push(r); + row_in_col[r] = c; + break; + } + } + } + + var highest_row = Math.max.apply(Math, rows); + + this.highest_occupied_cell = { + col: row_in_col[highest_row], + row: highest_row + }; + + return this.highest_occupied_cell; + }; + + + fn.get_widgets_from = function(col, row) { + var ga = this.gridmap; + var $widgets = $(); + + if (col) { + $widgets = $widgets.add( + this.$widgets.filter(function() { + var tcol = $(this).attr('data-col'); + return (tcol === col || tcol > col); + }) + ); + } + + if (row) { + $widgets = $widgets.add( + this.$widgets.filter(function() { + var trow = $(this).attr('data-row'); + return (trow === row || trow > row); + }) + ); + } + + return $widgets; + }; + + + /** + * Set the current height of the parent grid. + * + * @method set_dom_grid_height + * @return {Object} Returns the instance of the Gridster class. + */ + fn.set_dom_grid_height = function() { + var r = this.get_highest_occupied_cell().row; + this.$el.css('height', r * this.min_widget_height); + return this; + }; + + + /** + * It generates the neccessary styles to position the widgets. + * + * @method generate_stylesheet + * @param {Number} rows Number of columns. + * @param {Number} cols Number of rows. + * @return {Object} Returns the instance of the Gridster class. + */ + fn.generate_stylesheet = function(opts) { + var styles = ''; + var max_size_x = this.options.max_size_x; + var max_rows = 0; + var max_cols = 0; + var i; + var rules; + + opts || (opts = {}); + opts.cols || (opts.cols = this.cols); + opts.rows || (opts.rows = this.rows); + opts.namespace || (opts.namespace = this.options.namespace); + opts.widget_base_dimensions || + (opts.widget_base_dimensions = this.options.widget_base_dimensions); + opts.widget_margins || + (opts.widget_margins = this.options.widget_margins); + opts.min_widget_width = (opts.widget_margins[0] * 2) + + opts.widget_base_dimensions[0]; + opts.min_widget_height = (opts.widget_margins[1] * 2) + + opts.widget_base_dimensions[1]; + + // don't duplicate stylesheets for the same configuration + var serialized_opts = $.param(opts); + if ($.inArray(serialized_opts, Gridster.generated_stylesheets) >= 0) { + return false; + } + + Gridster.generated_stylesheets.push(serialized_opts); + + /* generate CSS styles for cols */ + for (i = opts.cols; i >= 0; i--) { + styles += (opts.namespace + ' [data-col="'+ (i + 1) + '"] { left:' + + ((i * opts.widget_base_dimensions[0]) + + (i * opts.widget_margins[0]) + + ((i + 1) * opts.widget_margins[0])) + 'px;} '); + } + + /* generate CSS styles for rows */ + for (i = opts.rows; i >= 0; i--) { + styles += (opts.namespace + ' [data-row="' + (i + 1) + '"] { top:' + + ((i * opts.widget_base_dimensions[1]) + + (i * opts.widget_margins[1]) + + ((i + 1) * opts.widget_margins[1]) ) + 'px;} '); + } + + for (var y = 1; y <= opts.rows; y++) { + styles += (opts.namespace + ' [data-sizey="' + y + '"] { height:' + + (y * opts.widget_base_dimensions[1] + + (y - 1) * (opts.widget_margins[1] * 2)) + 'px;}'); + } + + for (var x = 1; x <= max_size_x; x++) { + styles += (opts.namespace + ' [data-sizex="' + x + '"] { width:' + + (x * opts.widget_base_dimensions[0] + + (x - 1) * (opts.widget_margins[0] * 2)) + 'px;}'); + } + + return this.add_style_tag(styles); + }; + + + /** + * Injects the given CSS as string to the head of the document. + * + * @method add_style_tag + * @param {String} css The styles to apply. + * @return {Object} Returns the instance of the Gridster class. + */ + fn.add_style_tag = function(css) { + var d = document; + var tag = d.createElement('style'); + + d.getElementsByTagName('head')[0].appendChild(tag); + tag.setAttribute('type', 'text/css'); + + if (tag.styleSheet) { + tag.styleSheet.cssText = css; + }else{ + tag.appendChild(document.createTextNode(css)); + } + return this; + }; + + + /** + * Generates a faux grid to collide with it when a widget is dragged and + * detect row or column that we want to go. + * + * @method generate_faux_grid + * @param {Number} rows Number of columns. + * @param {Number} cols Number of rows. + * @return {Object} Returns the instance of the Gridster class. + */ + fn.generate_faux_grid = function(rows, cols) { + this.faux_grid = []; + this.gridmap = []; + var col; + var row; + for (col = cols; col > 0; col--) { + this.gridmap[col] = []; + for (row = rows; row > 0; row--) { + this.add_faux_cell(row, col); + } + } + return this; + }; + + + /** + * Add cell to the faux grid. + * + * @method add_faux_cell + * @param {Number} row The row for the new faux cell. + * @param {Number} col The col for the new faux cell. + * @return {Object} Returns the instance of the Gridster class. + */ + fn.add_faux_cell = function(row, col) { + var coords = $({ + left: this.baseX + ((col - 1) * this.min_widget_width), + top: this.baseY + (row -1) * this.min_widget_height, + width: this.min_widget_width, + height: this.min_widget_height, + col: col, + row: row, + original_col: col, + original_row: row + }).coords(); + + if (!$.isArray(this.gridmap[col])) { + this.gridmap[col] = []; + } + + this.gridmap[col][row] = false; + this.faux_grid.push(coords); + + return this; + }; + + + /** + * Add rows to the faux grid. + * + * @method add_faux_rows + * @param {Number} rows The number of rows you want to add to the faux grid. + * @return {Object} Returns the instance of the Gridster class. + */ + fn.add_faux_rows = function(rows) { + var actual_rows = this.rows; + var max_rows = actual_rows + (rows || 1); + + for (var r = max_rows; r > actual_rows; r--) { + for (var c = this.cols; c >= 1; c--) { + this.add_faux_cell(r, c); + } + } + + this.rows = max_rows; + + if (this.options.autogenerate_stylesheet) { + this.generate_stylesheet(); + } + + return this; + }; + + /** + * Add cols to the faux grid. + * + * @method add_faux_cols + * @param {Number} cols The number of cols you want to add to the faux grid. + * @return {Object} Returns the instance of the Gridster class. + */ + fn.add_faux_cols = function(cols) { + var actual_cols = this.cols; + var max_cols = actual_cols + (cols || 1); + + for (var c = actual_cols; c < max_cols; c++) { + for (var r = this.rows; r >= 1; r--) { + this.add_faux_cell(r, c); + } + } + + this.cols = max_cols; + + if (this.options.autogenerate_stylesheet) { + this.generate_stylesheet(); + } + + return this; + }; + + + /** + * Recalculates the offsets for the faux grid. You need to use it when + * the browser is resized. + * + * @method recalculate_faux_grid + * @return {Object} Returns the instance of the Gridster class. + */ + fn.recalculate_faux_grid = function() { + var aw = this.$wrapper.width(); + this.baseX = ($(window).width() - aw) / 2; + this.baseY = this.$wrapper.offset().top; + + $.each(this.faux_grid, $.proxy(function(i, coords) { + this.faux_grid[i] = coords.update({ + left: this.baseX + (coords.data.col -1) * this.min_widget_width, + top: this.baseY + (coords.data.row -1) * this.min_widget_height + }); + + }, this)); + + return this; + }; + + + /** + * Get all widgets in the DOM and register them. + * + * @method get_widgets_from_DOM + * @return {Object} Returns the instance of the Gridster class. + */ + fn.get_widgets_from_DOM = function() { + this.$widgets.each($.proxy(function(i, widget) { + this.register_widget($(widget)); + }, this)); + return this; + }; + + + /** + * Calculate columns and rows to be set based on the configuration + * parameters, grid dimensions, etc ... + * + * @method generate_grid_and_stylesheet + * @return {Object} Returns the instance of the Gridster class. + */ + fn.generate_grid_and_stylesheet = function() { + var aw = this.$wrapper.width(); + var ah = this.$wrapper.height(); + + var cols = Math.floor(aw / this.min_widget_width) + + this.options.extra_cols; + + var actual_cols = this.$widgets.map(function() { + return $(this).attr('data-col'); + }); + actual_cols = Array.prototype.slice.call(actual_cols, 0); + //needed to pass tests with phantomjs + actual_cols.length || (actual_cols = [0]); + + var min_cols = Math.max.apply(Math, actual_cols); + + // get all rows that could be occupied by the current widgets + var max_rows = this.options.extra_rows; + this.$widgets.each(function(i, w) { + max_rows += (+$(w).attr('data-sizey')); + }); + + this.cols = Math.max(min_cols, cols, this.options.min_cols); + this.rows = Math.max(max_rows, this.options.min_rows); + + this.baseX = ($(window).width() - aw) / 2; + this.baseY = this.$wrapper.offset().top; + + if (this.options.autogenerate_stylesheet) { + this.generate_stylesheet(); + } + + return this.generate_faux_grid(this.rows, this.cols); + }; + + + //jQuery adapter + $.fn.gridster = function(options) { + return this.each(function() { + if (!$(this).data('gridster')) { + $(this).data('gridster', new Gridster( this, options )); + } + }); + }; + + $.Gridster = fn; + +}(jQuery, window, document)); diff --git a/assets/jquery.js b/assets/jquery.js new file mode 100644 index 0000000..3774ff9 --- /dev/null +++ b/assets/jquery.js @@ -0,0 +1,9404 @@ +/*! + * jQuery JavaScript Library v1.7.2 + * http://jquery.com/ + * + * Copyright 2011, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Wed Mar 21 12:46:34 2012 -0700 + */ +(function( window, undefined ) { + +// Use the correct document accordingly with window argument (sandbox) +var document = window.document, + navigator = window.navigator, + location = window.location; +var jQuery = (function() { + +// Define a local copy of jQuery +var jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // A central reference to the root jQuery(document) + rootjQuery, + + // A simple way to check for HTML strings or ID strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, + + // Check if a string has a non-whitespace character in it + rnotwhite = /\S/, + + // Used for trimming whitespace + trimLeft = /^\s+/, + trimRight = /\s+$/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, + rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + + // Useragent RegExp + rwebkit = /(webkit)[ \/]([\w.]+)/, + ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, + rmsie = /(msie) ([\w.]+)/, + rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, + + // Matches dashed string for camelizing + rdashAlpha = /-([a-z]|[0-9])/ig, + rmsPrefix = /^-ms-/, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return ( letter + "" ).toUpperCase(); + }, + + // Keep a UserAgent string for use with jQuery.browser + userAgent = navigator.userAgent, + + // For matching the engine and version of the browser + browserMatch, + + // The deferred used on DOM ready + readyList, + + // The ready event handler + DOMContentLoaded, + + // Save a reference to some core methods + toString = Object.prototype.toString, + hasOwn = Object.prototype.hasOwnProperty, + push = Array.prototype.push, + slice = Array.prototype.slice, + trim = String.prototype.trim, + indexOf = Array.prototype.indexOf, + + // [[Class]] -> type pairs + class2type = {}; + +jQuery.fn = jQuery.prototype = { + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem, ret, doc; + + // Handle $(""), $(null), or $(undefined) + if ( !selector ) { + return this; + } + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // The body element only exists once, optimize finding it + if ( selector === "body" && !context && document.body ) { + this.context = document; + this[0] = document.body; + this.selector = selector; + this.length = 1; + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = quickExpr.exec( selector ); + } + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + doc = ( context ? context.ownerDocument || context : document ); + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + ret = rsingleTag.exec( selector ); + + if ( ret ) { + if ( jQuery.isPlainObject( context ) ) { + selector = [ document.createElement( ret[1] ) ]; + jQuery.fn.attr.call( selector, context, true ); + + } else { + selector = [ doc.createElement( ret[1] ) ]; + } + + } else { + ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); + selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes; + } + + return jQuery.merge( this, selector ); + + // HANDLE: $("#id") + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.7.2", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return slice.call( this, 0 ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new jQuery matched element set + var ret = this.constructor(); + + if ( jQuery.isArray( elems ) ) { + push.apply( ret, elems ); + + } else { + jQuery.merge( ret, elems ); + } + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) { + ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Attach the listeners + jQuery.bindReady(); + + // Add the callback + readyList.add( fn ); + + return this; + }, + + eq: function( i ) { + i = +i; + return i === -1 ? + this.slice( i ) : + this.slice( i, i + 1 ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + // Either a released hold or an DOMready/load event and not yet ready + if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready, 1 ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.fireWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger( "ready" ).off( "ready" ); + } + } + }, + + bindReady: function() { + if ( readyList ) { + return; + } + + readyList = jQuery.Callbacks( "once memory" ); + + // Catch cases where $(document).ready() is called after the + // browser event has already occurred. + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + return setTimeout( jQuery.ready, 1 ); + } + + // Mozilla, Opera and webkit nightlies currently support this event + if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else if ( document.attachEvent ) { + // ensure firing before onload, + // maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", DOMContentLoaded ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var toplevel = false; + + try { + toplevel = window.frameElement == null; + } catch(e) {} + + if ( document.documentElement.doScroll && toplevel ) { + doScrollCheck(); + } + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + isWindow: function( obj ) { + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + return !isNaN( parseFloat(obj) ) && isFinite( obj ); + }, + + type: function( obj ) { + return obj == null ? + String( obj ) : + class2type[ toString.call(obj) ] || "object"; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + for ( var name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw new Error( msg ); + }, + + parseJSON: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return ( new Function( "return " + data ) )(); + + } + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + parseXML: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + var xml, tmp; + try { + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && rnotwhite.test( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + }, + + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, + length = object.length, + isObj = length === undefined || jQuery.isFunction( object ); + + if ( args ) { + if ( isObj ) { + for ( name in object ) { + if ( callback.apply( object[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( object[ i++ ], args ) === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in object ) { + if ( callback.call( object[ name ], name, object[ name ] ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) { + break; + } + } + } + } + + return object; + }, + + // Use native String.trim function wherever possible + trim: trim ? + function( text ) { + return text == null ? + "" : + trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + text.toString().replace( trimLeft, "" ).replace( trimRight, "" ); + }, + + // results is for internal usage only + makeArray: function( array, results ) { + var ret = results || []; + + if ( array != null ) { + // The window, strings (and functions) also have 'length' + // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 + var type = jQuery.type( array ); + + if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) { + push.call( ret, array ); + } else { + jQuery.merge( ret, array ); + } + } + + return ret; + }, + + inArray: function( elem, array, i ) { + var len; + + if ( array ) { + if ( indexOf ) { + return indexOf.call( array, elem, i ); + } + + len = array.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in array && array[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var i = first.length, + j = 0; + + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var ret = [], retVal; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( var i = 0, length = elems.length; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, key, ret = [], + i = 0, + length = elems.length, + // jquery objects are treated as arrays + isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( key in elems ) { + value = callback( elems[ key ], key, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return ret.concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + if ( typeof context === "string" ) { + var tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + var args = slice.call( arguments, 2 ), + proxy = function() { + return fn.apply( context, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; + + return proxy; + }, + + // Mutifunctional method to get and set values to a collection + // The value/s can optionally be executed if it's a function + access: function( elems, fn, key, value, chainable, emptyGet, pass ) { + var exec, + bulk = key == null, + i = 0, + length = elems.length; + + // Sets many values + if ( key && typeof key === "object" ) { + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], 1, emptyGet, value ); + } + chainable = 1; + + // Sets one value + } else if ( value !== undefined ) { + // Optionally, function values get executed if exec is true + exec = pass === undefined && jQuery.isFunction( value ); + + if ( bulk ) { + // Bulk operations only iterate when executing function values + if ( exec ) { + exec = fn; + fn = function( elem, key, value ) { + return exec.call( jQuery( elem ), value ); + }; + + // Otherwise they run against the entire set + } else { + fn.call( elems, value ); + fn = null; + } + } + + if ( fn ) { + for (; i < length; i++ ) { + fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + } + } + + chainable = 1; + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[0], key ) : emptyGet; + }, + + now: function() { + return ( new Date() ).getTime(); + }, + + // Use of jQuery.browser is frowned upon. + // More details: http://docs.jquery.com/Utilities/jQuery.browser + uaMatch: function( ua ) { + ua = ua.toLowerCase(); + + var match = rwebkit.exec( ua ) || + ropera.exec( ua ) || + rmsie.exec( ua ) || + ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || + []; + + return { browser: match[1] || "", version: match[2] || "0" }; + }, + + sub: function() { + function jQuerySub( selector, context ) { + return new jQuerySub.fn.init( selector, context ); + } + jQuery.extend( true, jQuerySub, this ); + jQuerySub.superclass = this; + jQuerySub.fn = jQuerySub.prototype = this(); + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.sub = this.sub; + jQuerySub.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { + context = jQuerySub( context ); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); + }; + jQuerySub.fn.init.prototype = jQuerySub.fn; + var rootjQuerySub = jQuerySub(document); + return jQuerySub; + }, + + browser: {} +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +browserMatch = jQuery.uaMatch( userAgent ); +if ( browserMatch.browser ) { + jQuery.browser[ browserMatch.browser ] = true; + jQuery.browser.version = browserMatch.version; +} + +// Deprecated, use jQuery.browser.webkit instead +if ( jQuery.browser.webkit ) { + jQuery.browser.safari = true; +} + +// IE doesn't match non-breaking spaces with \s +if ( rnotwhite.test( "\xA0" ) ) { + trimLeft = /^[\s\xA0]+/; + trimRight = /[\s\xA0]+$/; +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); + +// Cleanup functions for the document ready method +if ( document.addEventListener ) { + DOMContentLoaded = function() { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + }; + +} else if ( document.attachEvent ) { + DOMContentLoaded = function() { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( document.readyState === "complete" ) { + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }; +} + +// The DOM ready check for Internet Explorer +function doScrollCheck() { + if ( jQuery.isReady ) { + return; + } + + try { + // If IE is used, use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + document.documentElement.doScroll("left"); + } catch(e) { + setTimeout( doScrollCheck, 1 ); + return; + } + + // and execute any waiting functions + jQuery.ready(); +} + +return jQuery; + +})(); + + +// String to Object flags format cache +var flagsCache = {}; + +// Convert String-formatted flags into Object-formatted ones and store in cache +function createFlags( flags ) { + var object = flagsCache[ flags ] = {}, + i, length; + flags = flags.split( /\s+/ ); + for ( i = 0, length = flags.length; i < length; i++ ) { + object[ flags[i] ] = true; + } + return object; +} + +/* + * Create a callback list using the following parameters: + * + * flags: an optional list of space-separated flags that will change how + * the callback list behaves + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible flags: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( flags ) { + + // Convert flags from String-formatted to Object-formatted + // (we check in cache first) + flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {}; + + var // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = [], + // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // Flag to know if list is currently firing + firing, + // First callback to fire (used internally by add and fireWith) + firingStart, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // Add one or several callbacks to the list + add = function( args ) { + var i, + length, + elem, + type, + actual; + for ( i = 0, length = args.length; i < length; i++ ) { + elem = args[ i ]; + type = jQuery.type( elem ); + if ( type === "array" ) { + // Inspect recursively + add( elem ); + } else if ( type === "function" ) { + // Add if not in unique mode and callback is not in + if ( !flags.unique || !self.has( elem ) ) { + list.push( elem ); + } + } + } + }, + // Fire callbacks + fire = function( context, args ) { + args = args || []; + memory = !flags.memory || [ context, args ]; + fired = true; + firing = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) { + memory = true; // Mark as halted + break; + } + } + firing = false; + if ( list ) { + if ( !flags.once ) { + if ( stack && stack.length ) { + memory = stack.shift(); + self.fireWith( memory[ 0 ], memory[ 1 ] ); + } + } else if ( memory === true ) { + self.disable(); + } else { + list = []; + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + var length = list.length; + add( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away, unless previous + // firing was halted (stopOnFalse) + } else if ( memory && memory !== true ) { + firingStart = length; + fire( memory[ 0 ], memory[ 1 ] ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + var args = arguments, + argIndex = 0, + argLength = args.length; + for ( ; argIndex < argLength ; argIndex++ ) { + for ( var i = 0; i < list.length; i++ ) { + if ( args[ argIndex ] === list[ i ] ) { + // Handle firingIndex and firingLength + if ( firing ) { + if ( i <= firingLength ) { + firingLength--; + if ( i <= firingIndex ) { + firingIndex--; + } + } + } + // Remove the element + list.splice( i--, 1 ); + // If we have some unicity property then + // we only need to do this once + if ( flags.unique ) { + break; + } + } + } + } + } + return this; + }, + // Control if a given callback is in the list + has: function( fn ) { + if ( list ) { + var i = 0, + length = list.length; + for ( ; i < length; i++ ) { + if ( fn === list[ i ] ) { + return true; + } + } + } + return false; + }, + // Remove all callbacks from the list + empty: function() { + list = []; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory || memory === true ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( stack ) { + if ( firing ) { + if ( !flags.once ) { + stack.push( [ context, args ] ); + } + } else if ( !( flags.once && memory ) ) { + fire( context, args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + + + +var // Static reference to slice + sliceDeferred = [].slice; + +jQuery.extend({ + + Deferred: function( func ) { + var doneList = jQuery.Callbacks( "once memory" ), + failList = jQuery.Callbacks( "once memory" ), + progressList = jQuery.Callbacks( "memory" ), + state = "pending", + lists = { + resolve: doneList, + reject: failList, + notify: progressList + }, + promise = { + done: doneList.add, + fail: failList.add, + progress: progressList.add, + + state: function() { + return state; + }, + + // Deprecated + isResolved: doneList.fired, + isRejected: failList.fired, + + then: function( doneCallbacks, failCallbacks, progressCallbacks ) { + deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks ); + return this; + }, + always: function() { + deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments ); + return this; + }, + pipe: function( fnDone, fnFail, fnProgress ) { + return jQuery.Deferred(function( newDefer ) { + jQuery.each( { + done: [ fnDone, "resolve" ], + fail: [ fnFail, "reject" ], + progress: [ fnProgress, "notify" ] + }, function( handler, data ) { + var fn = data[ 0 ], + action = data[ 1 ], + returned; + if ( jQuery.isFunction( fn ) ) { + deferred[ handler ](function() { + returned = fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); + } + }); + } else { + deferred[ handler ]( newDefer[ action ] ); + } + }); + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + if ( obj == null ) { + obj = promise; + } else { + for ( var key in promise ) { + obj[ key ] = promise[ key ]; + } + } + return obj; + } + }, + deferred = promise.promise({}), + key; + + for ( key in lists ) { + deferred[ key ] = lists[ key ].fire; + deferred[ key + "With" ] = lists[ key ].fireWith; + } + + // Handle state + deferred.done( function() { + state = "resolved"; + }, failList.disable, progressList.lock ).fail( function() { + state = "rejected"; + }, doneList.disable, progressList.lock ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( firstParam ) { + var args = sliceDeferred.call( arguments, 0 ), + i = 0, + length = args.length, + pValues = new Array( length ), + count = length, + pCount = length, + deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ? + firstParam : + jQuery.Deferred(), + promise = deferred.promise(); + function resolveFunc( i ) { + return function( value ) { + args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + if ( !( --count ) ) { + deferred.resolveWith( deferred, args ); + } + }; + } + function progressFunc( i ) { + return function( value ) { + pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + deferred.notifyWith( promise, pValues ); + }; + } + if ( length > 1 ) { + for ( ; i < length; i++ ) { + if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) { + args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) ); + } else { + --count; + } + } + if ( !count ) { + deferred.resolveWith( deferred, args ); + } + } else if ( deferred !== firstParam ) { + deferred.resolveWith( deferred, length ? [ firstParam ] : [] ); + } + return promise; + } +}); + + + + +jQuery.support = (function() { + + var support, + all, + a, + select, + opt, + input, + fragment, + tds, + events, + eventName, + i, + isSupported, + div = document.createElement( "div" ), + documentElement = document.documentElement; + + // Preliminary tests + div.setAttribute("className", "t"); + div.innerHTML = "
      a"; + + all = div.getElementsByTagName( "*" ); + a = div.getElementsByTagName( "a" )[ 0 ]; + + // Can't get basic test support + if ( !all || !all.length || !a ) { + return {}; + } + + // First batch of supports tests + select = document.createElement( "select" ); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName( "input" )[ 0 ]; + + support = { + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: ( div.firstChild.nodeType === 3 ), + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName("tbody").length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName("link").length, + + // Get the style information from getAttribute + // (IE uses .cssText instead) + style: /top/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: ( a.getAttribute("href") === "/a" ), + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.55/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Make sure that if no value is specified for a checkbox + // that it defaults to "on". + // (WebKit defaults to "" instead) + checkOn: ( input.value === "on" ), + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: opt.selected, + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + getSetAttribute: div.className !== "t", + + // Tests for enctype support on a form(#6743) + enctype: !!document.createElement("form").enctype, + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>", + + // Will be defined later + submitBubbles: true, + changeBubbles: true, + focusinBubbles: false, + deleteExpando: true, + noCloneEvent: true, + inlineBlockNeedsLayout: false, + shrinkWrapBlocks: false, + reliableMarginRight: true, + pixelMargin: true + }; + + // jQuery.boxModel DEPRECATED in 1.3, use jQuery.support.boxModel instead + jQuery.boxModel = support.boxModel = (document.compatMode === "CSS1Compat"); + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Test to see if it's possible to delete an expando from an element + // Fails in Internet Explorer + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { + div.attachEvent( "onclick", function() { + // Cloning a node shouldn't copy over any + // bound event handlers (IE does this) + support.noCloneEvent = false; + }); + div.cloneNode( true ).fireEvent( "onclick" ); + } + + // Check if a radio maintains its value + // after being appended to the DOM + input = document.createElement("input"); + input.value = "t"; + input.setAttribute("type", "radio"); + support.radioValue = input.value === "t"; + + input.setAttribute("checked", "checked"); + + // #11217 - WebKit loses check when the name is after the checked attribute + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + fragment = document.createDocumentFragment(); + fragment.appendChild( div.lastChild ); + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + fragment.removeChild( input ); + fragment.appendChild( div ); + + // Technique from Juriy Zaytsev + // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ + // We only care about the case where non-standard event systems + // are used, namely in IE. Short-circuiting here helps us to + // avoid an eval call (in setAttribute) which can cause CSP + // to go haywire. See: https://developer.mozilla.org/en/Security/CSP + if ( div.attachEvent ) { + for ( i in { + submit: 1, + change: 1, + focusin: 1 + }) { + eventName = "on" + i; + isSupported = ( eventName in div ); + if ( !isSupported ) { + div.setAttribute( eventName, "return;" ); + isSupported = ( typeof div[ eventName ] === "function" ); + } + support[ i + "Bubbles" ] = isSupported; + } + } + + fragment.removeChild( div ); + + // Null elements to avoid leaks in IE + fragment = select = opt = div = input = null; + + // Run tests that need a body at doc ready + jQuery(function() { + var container, outer, inner, table, td, offsetSupport, + marginDiv, conMarginTop, style, html, positionTopLeftWidthHeight, + paddingMarginBorderVisibility, paddingMarginBorder, + body = document.getElementsByTagName("body")[0]; + + if ( !body ) { + // Return for frameset docs that don't have a body + return; + } + + conMarginTop = 1; + paddingMarginBorder = "padding:0;margin:0;border:"; + positionTopLeftWidthHeight = "position:absolute;top:0;left:0;width:1px;height:1px;"; + paddingMarginBorderVisibility = paddingMarginBorder + "0;visibility:hidden;"; + style = "style='" + positionTopLeftWidthHeight + paddingMarginBorder + "5px solid #000;"; + html = "
      " + + "" + + "
      "; + + container = document.createElement("div"); + container.style.cssText = paddingMarginBorderVisibility + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px"; + body.insertBefore( container, body.firstChild ); + + // Construct the test element + div = document.createElement("div"); + container.appendChild( div ); + + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + // (only IE 8 fails this test) + div.innerHTML = "
      t
      "; + tds = div.getElementsByTagName( "td" ); + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Check if empty table cells still have offsetWidth/Height + // (IE <= 8 fail this test) + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. For more + // info see bug #3333 + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + if ( window.getComputedStyle ) { + div.innerHTML = ""; + marginDiv = document.createElement( "div" ); + marginDiv.style.width = "0"; + marginDiv.style.marginRight = "0"; + div.style.width = "2px"; + div.appendChild( marginDiv ); + support.reliableMarginRight = + ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; + } + + if ( typeof div.style.zoom !== "undefined" ) { + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + // (IE < 8 does this) + div.innerHTML = ""; + div.style.width = div.style.padding = "1px"; + div.style.border = 0; + div.style.overflow = "hidden"; + div.style.display = "inline"; + div.style.zoom = 1; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); + + // Check if elements with layout shrink-wrap their children + // (IE 6 does this) + div.style.display = "block"; + div.style.overflow = "visible"; + div.innerHTML = "
      "; + support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); + } + + div.style.cssText = positionTopLeftWidthHeight + paddingMarginBorderVisibility; + div.innerHTML = html; + + outer = div.firstChild; + inner = outer.firstChild; + td = outer.nextSibling.firstChild.firstChild; + + offsetSupport = { + doesNotAddBorder: ( inner.offsetTop !== 5 ), + doesAddBorderForTableAndCells: ( td.offsetTop === 5 ) + }; + + inner.style.position = "fixed"; + inner.style.top = "20px"; + + // safari subtracts parent border width here which is 5px + offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 ); + inner.style.position = inner.style.top = ""; + + outer.style.overflow = "hidden"; + outer.style.position = "relative"; + + offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 ); + offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop ); + + if ( window.getComputedStyle ) { + div.style.marginTop = "1%"; + support.pixelMargin = ( window.getComputedStyle( div, null ) || { marginTop: 0 } ).marginTop !== "1%"; + } + + if ( typeof container.style.zoom !== "undefined" ) { + container.style.zoom = 1; + } + + body.removeChild( container ); + marginDiv = div = container = null; + + jQuery.extend( support, offsetSupport ); + }); + + return support; +})(); + + + + +var rbrace = /^(?:\{.*\}|\[.*\])$/, + rmultiDash = /([A-Z])/g; + +jQuery.extend({ + cache: {}, + + // Please use with caution + uuid: 0, + + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var privateCache, thisCache, ret, + internalKey = jQuery.expando, + getByName = typeof name === "string", + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey, + isEvents = name === "events"; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + elem[ internalKey ] = id = ++jQuery.uuid; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + cache[ id ] = {}; + + // Avoids exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + privateCache = thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Users should not attempt to inspect the internal events object using jQuery.data, + // it is undocumented and subject to change. But does anyone listen? No. + if ( isEvents && !thisCache[ name ] ) { + return privateCache.events; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( getByName ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; + }, + + removeData: function( elem, name, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, i, l, + + // Reference to internal data cache key + internalKey = jQuery.expando, + + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + + // See jQuery.data for more information + id = isNode ? elem[ internalKey ] : internalKey; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split( " " ); + } + } + } + + for ( i = 0, l = name.length; i < l; i++ ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject(cache[ id ]) ) { + return; + } + } + + // Browsers that fail expando deletion also refuse to delete expandos on + // the window, but it will allow it on all other JS objects; other browsers + // don't care + // Ensure that `cache` is not a window object #10080 + if ( jQuery.support.deleteExpando || !cache.setInterval ) { + delete cache[ id ]; + } else { + cache[ id ] = null; + } + + // We destroyed the cache and need to eliminate the expando on the node to avoid + // false lookups in the cache for entries that no longer exist + if ( isNode ) { + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( jQuery.support.deleteExpando ) { + delete elem[ internalKey ]; + } else if ( elem.removeAttribute ) { + elem.removeAttribute( internalKey ); + } else { + elem[ internalKey ] = null; + } + } + }, + + // For internal use only. + _data: function( elem, name, data ) { + return jQuery.data( elem, name, data, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + if ( elem.nodeName ) { + var match = jQuery.noData[ elem.nodeName.toLowerCase() ]; + + if ( match ) { + return !(match === true || elem.getAttribute("classid") !== match); + } + } + + return true; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var parts, part, attr, name, l, + elem = this[0], + i = 0, + data = null; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + attr = elem.attributes; + for ( l = attr.length; i < l; i++ ) { + name = attr[i].name; + + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.substring(5) ); + + dataAttr( elem, name, data[ name ] ); + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + parts = key.split( ".", 2 ); + parts[1] = parts[1] ? "." + parts[1] : ""; + part = parts[1] + "!"; + + return jQuery.access( this, function( value ) { + + if ( value === undefined ) { + data = this.triggerHandler( "getData" + part, [ parts[0] ] ); + + // Try to fetch any internally stored data first + if ( data === undefined && elem ) { + data = jQuery.data( elem, key ); + data = dataAttr( elem, key, data ); + } + + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + } + + parts[1] = value; + this.each(function() { + var self = jQuery( this ); + + self.triggerHandler( "setData" + part, parts ); + jQuery.data( this, key, value ); + self.triggerHandler( "changeData" + part, parts ); + }); + }, null, value, arguments.length > 1, null, false ); + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + jQuery.isNumeric( data ) ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + for ( var name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} + + + + +function handleQueueMarkDefer( elem, type, src ) { + var deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + defer = jQuery._data( elem, deferDataKey ); + if ( defer && + ( src === "queue" || !jQuery._data(elem, queueDataKey) ) && + ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) { + // Give room for hard-coded callbacks to fire first + // and eventually mark/queue something else on the element + setTimeout( function() { + if ( !jQuery._data( elem, queueDataKey ) && + !jQuery._data( elem, markDataKey ) ) { + jQuery.removeData( elem, deferDataKey, true ); + defer.fire(); + } + }, 0 ); + } +} + +jQuery.extend({ + + _mark: function( elem, type ) { + if ( elem ) { + type = ( type || "fx" ) + "mark"; + jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 ); + } + }, + + _unmark: function( force, elem, type ) { + if ( force !== true ) { + type = elem; + elem = force; + force = false; + } + if ( elem ) { + type = type || "fx"; + var key = type + "mark", + count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 ); + if ( count ) { + jQuery._data( elem, key, count ); + } else { + jQuery.removeData( elem, key, true ); + handleQueueMarkDefer( elem, type, "mark" ); + } + } + }, + + queue: function( elem, type, data ) { + var q; + if ( elem ) { + type = ( type || "fx" ) + "queue"; + q = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !q || jQuery.isArray(data) ) { + q = jQuery._data( elem, type, jQuery.makeArray(data) ); + } else { + q.push( data ); + } + } + return q || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + fn = queue.shift(), + hooks = {}; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + } + + if ( fn ) { + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + jQuery._data( elem, type + ".run", hooks ); + fn.call( elem, function() { + jQuery.dequeue( elem, type ); + }, hooks ); + } + + if ( !queue.length ) { + jQuery.removeData( elem, type + "queue " + type + ".run", true ); + handleQueueMarkDefer( elem, type, "queue" ); + } + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, object ) { + if ( typeof type !== "string" ) { + object = type; + type = undefined; + } + type = type || "fx"; + var defer = jQuery.Deferred(), + elements = this, + i = elements.length, + count = 1, + deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + tmp; + function resolve() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + } + while( i-- ) { + if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) || + ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) || + jQuery.data( elements[ i ], markDataKey, undefined, true ) ) && + jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) { + count++; + tmp.add( resolve ); + } + } + resolve(); + return defer.promise( object ); + } +}); + + + + +var rclass = /[\n\t\r]/g, + rspace = /\s+/, + rreturn = /\r/g, + rtype = /^(?:button|input)$/i, + rfocusable = /^(?:button|input|object|select|textarea)$/i, + rclickable = /^a(?:rea)?$/i, + rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, + getSetAttribute = jQuery.support.getSetAttribute, + nodeHook, boolHook, fixSpecified; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classNames, i, l, elem, + setClass, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call(this, j, this.className) ); + }); + } + + if ( value && typeof value === "string" ) { + classNames = value.split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 ) { + if ( !elem.className && classNames.length === 1 ) { + elem.className = value; + + } else { + setClass = " " + elem.className + " "; + + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { + setClass += classNames[ c ] + " "; + } + } + elem.className = jQuery.trim( setClass ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classNames, i, l, elem, className, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call(this, j, this.className) ); + }); + } + + if ( (value && typeof value === "string") || value === undefined ) { + classNames = ( value || "" ).split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 && elem.className ) { + if ( value ) { + className = (" " + elem.className + " ").replace( rclass, " " ); + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + className = className.replace(" " + classNames[ c ] + " ", " "); + } + elem.className = jQuery.trim( className ); + + } else { + elem.className = ""; + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.split( rspace ); + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space seperated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + } else if ( type === "undefined" || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // toggle whole className + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var hooks, ret, isFunction, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var self = jQuery(this), val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, self.val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + }, + select: { + get: function( elem ) { + var value, i, max, option, + index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type === "select-one"; + + // Nothing was selected + if ( index < 0 ) { + return null; + } + + // Loop through all the selected options + i = one ? index : 0; + max = one ? index + 1 : options.length; + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Don't return options that are disabled or in a disabled optgroup + if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && + (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + // Fixes Bug #2551 -- select.val() broken in IE after form.reset() + if ( one && !values.length && options.length ) { + return jQuery( options[ index ] ).val(); + } + + return values; + }, + + set: function( elem, value ) { + var values = jQuery.makeArray( value ); + + jQuery(elem).find("option").each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + attrFn: { + val: true, + css: true, + html: true, + text: true, + data: true, + width: true, + height: true, + offset: true + }, + + attr: function( elem, name, value, pass ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( pass && name in jQuery.attrFn ) { + return jQuery( elem )[ name ]( value ); + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( notxml ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + + } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, "" + value ); + return value; + } + + } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + + ret = elem.getAttribute( name ); + + // Non-existent attributes return null, we normalize to undefined + return ret === null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var propName, attrNames, name, l, isBool, + i = 0; + + if ( value && elem.nodeType === 1 ) { + attrNames = value.toLowerCase().split( rspace ); + l = attrNames.length; + + for ( ; i < l; i++ ) { + name = attrNames[ i ]; + + if ( name ) { + propName = jQuery.propFix[ name ] || name; + isBool = rboolean.test( name ); + + // See #9699 for explanation of this approach (setting first, then removal) + // Do not do this for boolean attributes (see #10870) + if ( !isBool ) { + jQuery.attr( elem, name, "" ); + } + elem.removeAttribute( getSetAttribute ? name : propName ); + + // Set corresponding property to false for boolean attributes + if ( isBool && propName in elem ) { + elem[ propName ] = false; + } + } + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + // We can't allow the type property to be changed (since it causes problems in IE) + if ( rtype.test( elem.nodeName ) && elem.parentNode ) { + jQuery.error( "type property can't be changed" ); + } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to it's default in case type is set after value + // This is for element creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + }, + // Use the value property for back compat + // Use the nodeHook for button elements in IE6/7 (#1954) + value: { + get: function( elem, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.get( elem, name ); + } + return name in elem ? + elem.value : + null; + }, + set: function( elem, value, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.set( elem, value, name ); + } + // Does not return so that setAttribute is also used + elem.value = value; + } + } + }, + + propFix: { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + cellpadding: "cellPadding", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder", + contenteditable: "contentEditable" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + return ( elem[ name ] = value ); + } + + } else { + if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + var attributeNode = elem.getAttributeNode("tabindex"); + + return attributeNode && attributeNode.specified ? + parseInt( attributeNode.value, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + } + } +}); + +// Add the tabIndex propHook to attrHooks for back-compat (different case is intentional) +jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex; + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + // Fall back to attribute presence where some booleans are not supported + var attrNode, + property = jQuery.prop( elem, name ); + return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + var propName; + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + // value is true since we know at this point it's type boolean and not false + // Set boolean attributes to the same name and set the DOM property + propName = jQuery.propFix[ name ] || name; + if ( propName in elem ) { + // Only set the IDL specifically if it already exists on the element + elem[ propName ] = true; + } + + elem.setAttribute( name, name.toLowerCase() ); + } + return name; + } +}; + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !getSetAttribute ) { + + fixSpecified = { + name: true, + id: true, + coords: true + }; + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = jQuery.valHooks.button = { + get: function( elem, name ) { + var ret; + ret = elem.getAttributeNode( name ); + return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ? + ret.nodeValue : + undefined; + }, + set: function( elem, value, name ) { + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + ret = document.createAttribute( name ); + elem.setAttributeNode( ret ); + } + return ( ret.nodeValue = value + "" ); + } + }; + + // Apply the nodeHook to tabindex + jQuery.attrHooks.tabindex.set = nodeHook.set; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }); + }); + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + get: nodeHook.get, + set: function( elem, value, name ) { + if ( value === "" ) { + value = "false"; + } + nodeHook.set( elem, value, name ); + } + }; +} + + +// Some attributes require a special call on IE +if ( !jQuery.support.hrefNormalized ) { + jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + get: function( elem ) { + var ret = elem.getAttribute( name, 2 ); + return ret === null ? undefined : ret; + } + }); + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Normalize to lowercase since IE uppercases css property names + return elem.style.cssText.toLowerCase() || undefined; + }, + set: function( elem, value ) { + return ( elem.style.cssText = "" + value ); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + } + }); +} + +// IE6/7 call enctype encoding +if ( !jQuery.support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + +// Radios and checkboxes getter/setter +if ( !jQuery.support.checkOn ) { + jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + get: function( elem ) { + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + } + }; + }); +} +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }); +}); + + + + +var rformElems = /^(?:textarea|input|select)$/i, + rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/, + rhoverHack = /(?:^|\s)hover(\.\S+)?\b/, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/, + quickParse = function( selector ) { + var quick = rquickIs.exec( selector ); + if ( quick ) { + // 0 1 2 3 + // [ _, tag, id, class ] + quick[1] = ( quick[1] || "" ).toLowerCase(); + quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" ); + } + return quick; + }, + quickIs = function( elem, m ) { + var attrs = elem.attributes || {}; + return ( + (!m[1] || elem.nodeName.toLowerCase() === m[1]) && + (!m[2] || (attrs.id || {}).value === m[2]) && + (!m[3] || m[3].test( (attrs[ "class" ] || {}).value )) + ); + }, + hoverHack = function( events ) { + return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" ); + }; + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + add: function( elem, types, handler, data, selector ) { + + var elemData, eventHandle, events, + t, tns, type, namespaces, handleObj, + handleObjIn, quick, handlers, special; + + // Don't attach events to noData or text/comment nodes (allow plain objects tho) + if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + events = elemData.events; + if ( !events ) { + elemData.events = events = {}; + } + eventHandle = elemData.handle; + if ( !eventHandle ) { + elemData.handle = eventHandle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = jQuery.trim( hoverHack(types) ).split( " " ); + for ( t = 0; t < types.length; t++ ) { + + tns = rtypenamespace.exec( types[t] ) || []; + type = tns[1]; + namespaces = ( tns[2] || "" ).split( "." ).sort(); + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: tns[1], + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + quick: selector && quickParse( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + handlers = events[ type ]; + if ( !handlers ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + global: {}, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var elemData = jQuery.hasData( elem ) && jQuery._data( elem ), + t, tns, type, origType, namespaces, origCount, + j, events, special, handle, eventType, handleObj; + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = jQuery.trim( hoverHack( types || "" ) ).split(" "); + for ( t = 0; t < types.length; t++ ) { + tns = rtypenamespace.exec( types[t] ) || []; + type = origType = tns[1]; + namespaces = tns[2]; + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector? special.delegateType : special.bindType ) || type; + eventType = events[ type ] || []; + origCount = eventType.length; + namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null; + + // Remove matching events + for ( j = 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !namespaces || namespaces.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + eventType.splice( j--, 1 ); + + if ( handleObj.selector ) { + eventType.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( eventType.length === 0 && origCount !== eventType.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + handle = elemData.handle; + if ( handle ) { + handle.elem = null; + } + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery.removeData( elem, [ "events", "handle" ], true ); + } + }, + + // Events that are safe to short-circuit if no handlers are attached. + // Native DOM events should not be added, they may have inline handlers. + customEvent: { + "getData": true, + "setData": true, + "changeData": true + }, + + trigger: function( event, data, elem, onlyHandlers ) { + // Don't do events on text and comment nodes + if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) { + return; + } + + // Event object or event type + var type = event.type || event, + namespaces = [], + cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType; + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "!" ) >= 0 ) { + // Exclusive events trigger only for the exact event (no namespaces) + type = type.slice(0, -1); + exclusive = true; + } + + if ( type.indexOf( "." ) >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + + if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { + // No jQuery handlers for this event type, and it can't have inline handlers + return; + } + + // Caller can pass in an Event, Object, or just an event type string + event = typeof event === "object" ? + // jQuery.Event object + event[ jQuery.expando ] ? event : + // Object literal + new jQuery.Event( type, event ) : + // Just the event type (string) + new jQuery.Event( type ); + + event.type = type; + event.isTrigger = true; + event.exclusive = exclusive; + event.namespace = namespaces.join( "." ); + event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null; + ontype = type.indexOf( ":" ) < 0 ? "on" + type : ""; + + // Handle a global trigger + if ( !elem ) { + + // TODO: Stop taunting the data cache; remove global events and always attach to document + cache = jQuery.cache; + for ( i in cache ) { + if ( cache[ i ].events && cache[ i ].events[ type ] ) { + jQuery.event.trigger( event, data, cache[ i ].handle.elem, true ); + } + } + return; + } + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data != null ? jQuery.makeArray( data ) : []; + data.unshift( event ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + eventPath = [[ elem, special.bindType || type ]]; + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode; + old = null; + for ( ; cur; cur = cur.parentNode ) { + eventPath.push([ cur, bubbleType ]); + old = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( old && old === elem.ownerDocument ) { + eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]); + } + } + + // Fire handlers on the event path + for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) { + + cur = eventPath[i][0]; + event.type = eventPath[i][1]; + + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + // Note that this is a bare JS function and not a jQuery handler + handle = ontype && cur[ ontype ]; + if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) { + event.preventDefault(); + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && + !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + // IE<9 dies on focus/blur to hidden element (#1486) + if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + old = elem[ ontype ]; + + if ( old ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + elem[ type ](); + jQuery.event.triggered = undefined; + + if ( old ) { + elem[ ontype ] = old; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event || window.event ); + + var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []), + delegateCount = handlers.delegateCount, + args = [].slice.call( arguments, 0 ), + run_all = !event.exclusive && !event.namespace, + special = jQuery.event.special[ event.type ] || {}, + handlerQueue = [], + i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers that should run if there are delegated events + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && !(event.button && event.type === "click") ) { + + // Pregenerate a single jQuery object for reuse with .is() + jqcur = jQuery(this); + jqcur.context = this.ownerDocument || this; + + for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { + + // Don't process events on disabled elements (#6911, #8165) + if ( cur.disabled !== true ) { + selMatch = {}; + matches = []; + jqcur[0] = cur; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + sel = handleObj.selector; + + if ( selMatch[ sel ] === undefined ) { + selMatch[ sel ] = ( + handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel ) + ); + } + if ( selMatch[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, matches: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( handlers.length > delegateCount ) { + handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) }); + } + + // Run delegates first; they may want to stop propagation beneath us + for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) { + matched = handlerQueue[ i ]; + event.currentTarget = matched.elem; + + for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) { + handleObj = matched.matches[ j ]; + + // Triggered event must either 1) be non-exclusive and have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) { + + event.data = handleObj.data; + event.handleObj = handleObj; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + event.result = ret; + if ( ret === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 *** + props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, + originalEvent = event, + fixHook = jQuery.event.fixHooks[ event.type ] || {}, + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = jQuery.Event( originalEvent ); + + for ( i = copy.length; i; ) { + prop = copy[ --i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Target should not be a text node (#504, Safari) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8) + if ( event.metaKey === undefined ) { + event.metaKey = event.ctrlKey; + } + + return fixHook.filter? fixHook.filter( event, originalEvent ) : event; + }, + + special: { + ready: { + // Make sure the ready event is setup + setup: jQuery.bindReady + }, + + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + + focus: { + delegateType: "focusin" + }, + blur: { + delegateType: "focusout" + }, + + beforeunload: { + setup: function( data, namespaces, eventHandle ) { + // We only want to do this special case on windows + if ( jQuery.isWindow( this ) ) { + this.onbeforeunload = eventHandle; + } + }, + + teardown: function( namespaces, eventHandle ) { + if ( this.onbeforeunload === eventHandle ) { + this.onbeforeunload = null; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +// Some plugins are using, but it's undocumented/deprecated and will be removed. +// The 1.7 special event interface should provide all the hooks needed now. +jQuery.event.handle = jQuery.event.dispatch; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + if ( elem.detachEvent ) { + elem.detachEvent( "on" + type, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +function returnFalse() { + return false; +} +function returnTrue() { + return true; +} + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + preventDefault: function() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + + // if preventDefault exists run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // otherwise set the returnValue property of the original event to false (IE) + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + // if stopPropagation exists run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + // otherwise set the cancelBubble property of the original event to true (IE) + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var target = this, + related = event.relatedTarget, + handleObj = event.handleObj, + selector = handleObj.selector, + ret; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// IE submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !form._submit_attached ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submit_bubble = true; + }); + form._submit_attached = true; + } + }); + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( event._submit_bubble ) { + delete event._submit_bubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + } + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !jQuery.support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + jQuery.event.simulate( "change", this, event, true ); + } + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + elem._change_attached = true; + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0, + handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { // && selector != null + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + var handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( var type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + bind: function( types, data, fn ) { + return this.on( types, null, data, fn ); + }, + unbind: function( types, fn ) { + return this.off( types, null, fn ); + }, + + live: function( types, data, fn ) { + jQuery( this.context ).on( types, this.selector, data, fn ); + return this; + }, + die: function( types, fn ) { + jQuery( this.context ).off( types, this.selector || "**", fn ); + return this; + }, + + delegate: function( selector, types, data, fn ) { + return this.on( types, selector, data, fn ); + }, + undelegate: function( selector, types, fn ) { + // ( namespace ) or ( selector, types [, fn] ) + return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn ); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + if ( this[0] ) { + return jQuery.event.trigger( type, data, this[0], true ); + } + }, + + toggle: function( fn ) { + // Save reference to arguments for access in closure + var args = arguments, + guid = fn.guid || jQuery.guid++, + i = 0, + toggler = function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + }; + + // link all the functions, so any of them can unbind this click handler + toggler.guid = guid; + while ( i < args.length ) { + args[ i++ ].guid = guid; + } + + return this.click( toggler ); + }, + + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +}); + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.on( name, null, data, fn ) : + this.trigger( name ); + }; + + if ( jQuery.attrFn ) { + jQuery.attrFn[ name ] = true; + } + + if ( rkeyEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks; + } + + if ( rmouseEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; + } +}); + + + +/*! + * Sizzle CSS Selector Engine + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + expando = "sizcache" + (Math.random() + '').replace('.', ''), + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true, + rBackslash = /\\/g, + rReturn = /\r\n/g, + rNonWord = /\W/; + +// Here we check if the JavaScript engine is using some sort of +// optimization where it does not always call our comparision +// function. If that is the case, discard the hasDuplicate value. +// Thus far that includes Google Chrome. +[0, 0].sort(function() { + baseHasDuplicate = false; + return 0; +}); + +var Sizzle = function( selector, context, results, seed ) { + results = results || []; + context = context || document; + + var origContext = context; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var m, set, checkSet, extra, ret, cur, pop, i, + prune = true, + contextXML = Sizzle.isXML( context ), + parts = [], + soFar = selector; + + // Reset the position of the chunker regexp (start from head) + do { + chunker.exec( "" ); + m = chunker.exec( soFar ); + + if ( m ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + } while ( m ); + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context, seed ); + + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) { + selector += parts.shift(); + } + + set = posProcess( selector, set, seed ); + } + } + + } else { + // Take a shortcut and set the context if the root selector is an ID + // (but not if it'll be faster if the inner selector is an ID) + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + + ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? + Sizzle.filter( ret.expr, ret.set )[0] : + ret.set[0]; + } + + if ( context ) { + ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + + set = ret.expr ? + Sizzle.filter( ret.expr, ret.set ) : + ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray( set ); + + } else { + prune = false; + } + + while ( parts.length ) { + cur = parts.pop(); + pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + Sizzle.error( cur || selector ); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + + } else if ( context && context.nodeType === 1 ) { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + + } else { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function( results ) { + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[ i - 1 ] ) { + results.splice( i--, 1 ); + } + } + } + } + + return results; +}; + +Sizzle.matches = function( expr, set ) { + return Sizzle( expr, null, null, set ); +}; + +Sizzle.matchesSelector = function( node, expr ) { + return Sizzle( expr, null, null, [node] ).length > 0; +}; + +Sizzle.find = function( expr, context, isXML ) { + var set, i, len, match, type, left; + + if ( !expr ) { + return []; + } + + for ( i = 0, len = Expr.order.length; i < len; i++ ) { + type = Expr.order[i]; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + left = match[1]; + match.splice( 1, 1 ); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace( rBackslash, "" ); + set = Expr.find[ type ]( match, context, isXML ); + + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( "*" ) : + []; + } + + return { set: set, expr: expr }; +}; + +Sizzle.filter = function( expr, set, inplace, not ) { + var match, anyFound, + type, found, item, filter, left, + i, pass, + old = expr, + result = [], + curLoop = set, + isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); + + while ( expr && set.length ) { + for ( type in Expr.filter ) { + if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { + filter = Expr.filter[ type ]; + left = match[1]; + + anyFound = false; + + match.splice(1,1); + + if ( left.substr( left.length - 1 ) === "\\" ) { + continue; + } + + if ( curLoop === result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + pass = not ^ found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + + } else { + curLoop[i] = false; + } + + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + // Improper expression + if ( expr === old ) { + if ( anyFound == null ) { + Sizzle.error( expr ); + + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Utility function for retreiving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +var getText = Sizzle.getText = function( elem ) { + var i, node, + nodeType = elem.nodeType, + ret = ""; + + if ( nodeType ) { + if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent || innerText for elements + if ( typeof elem.textContent === 'string' ) { + return elem.textContent; + } else if ( typeof elem.innerText === 'string' ) { + // Replace IE's carriage returns + return elem.innerText.replace( rReturn, '' ); + } else { + // Traverse it's children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + } else { + + // If no nodeType, this is expected to be an array + for ( i = 0; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + if ( node.nodeType !== 8 ) { + ret += getText( node ); + } + } + } + return ret; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + + match: { + ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ + }, + + leftMatch: {}, + + attrMap: { + "class": "className", + "for": "htmlFor" + }, + + attrHandle: { + href: function( elem ) { + return elem.getAttribute( "href" ); + }, + type: function( elem ) { + return elem.getAttribute( "type" ); + } + }, + + relative: { + "+": function(checkSet, part){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !rNonWord.test( part ), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag ) { + part = part.toLowerCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + + ">": function( checkSet, part ) { + var elem, + isPartStr = typeof part === "string", + i = 0, + l = checkSet.length; + + if ( isPartStr && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } + + } else { + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + + "": function(checkSet, part, isXML){ + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); + }, + + "~": function( checkSet, part, isXML ) { + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); + } + }, + + find: { + ID: function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }, + + NAME: function( match, context ) { + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], + results = context.getElementsByName( match[1] ); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + + TAG: function( match, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( match[1] ); + } + } + }, + preFilter: { + CLASS: function( match, curLoop, inplace, result, not, isXML ) { + match = " " + match[1].replace( rBackslash, "" ) + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { + if ( !inplace ) { + result.push( elem ); + } + + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + + ID: function( match ) { + return match[1].replace( rBackslash, "" ); + }, + + TAG: function( match, curLoop ) { + return match[1].replace( rBackslash, "" ).toLowerCase(); + }, + + CHILD: function( match ) { + if ( match[1] === "nth" ) { + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + match[2] = match[2].replace(/^\+|\s*/g, ''); + + // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' + var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( + match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + // calculate the numbers (first)n+(last) including if they are negative + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + // TODO: Move to normal caching system + match[0] = done++; + + return match; + }, + + ATTR: function( match, curLoop, inplace, result, not, isXML ) { + var name = match[1] = match[1].replace( rBackslash, "" ); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + // Handle if an un-quoted value was used + match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + + PSEUDO: function( match, curLoop, inplace, result, not ) { + if ( match[1] === "not" ) { + // If we're dealing with a complex expression, or a simple one + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + + if ( !inplace ) { + result.push.apply( result, ret ); + } + + return false; + } + + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + + POS: function( match ) { + match.unshift( true ); + + return match; + } + }, + + filters: { + enabled: function( elem ) { + return elem.disabled === false && elem.type !== "hidden"; + }, + + disabled: function( elem ) { + return elem.disabled === true; + }, + + checked: function( elem ) { + return elem.checked === true; + }, + + selected: function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + parent: function( elem ) { + return !!elem.firstChild; + }, + + empty: function( elem ) { + return !elem.firstChild; + }, + + has: function( elem, i, match ) { + return !!Sizzle( match[3], elem ).length; + }, + + header: function( elem ) { + return (/h\d/i).test( elem.nodeName ); + }, + + text: function( elem ) { + var attr = elem.getAttribute( "type" ), type = elem.type; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); + }, + + radio: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; + }, + + checkbox: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; + }, + + file: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; + }, + + password: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; + }, + + submit: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "submit" === elem.type; + }, + + image: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; + }, + + reset: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "reset" === elem.type; + }, + + button: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && "button" === elem.type || name === "button"; + }, + + input: function( elem ) { + return (/input|select|textarea|button/i).test( elem.nodeName ); + }, + + focus: function( elem ) { + return elem === elem.ownerDocument.activeElement; + } + }, + setFilters: { + first: function( elem, i ) { + return i === 0; + }, + + last: function( elem, i, match, array ) { + return i === array.length - 1; + }, + + even: function( elem, i ) { + return i % 2 === 0; + }, + + odd: function( elem, i ) { + return i % 2 === 1; + }, + + lt: function( elem, i, match ) { + return i < match[3] - 0; + }, + + gt: function( elem, i, match ) { + return i > match[3] - 0; + }, + + nth: function( elem, i, match ) { + return match[3] - 0 === i; + }, + + eq: function( elem, i, match ) { + return match[3] - 0 === i; + } + }, + filter: { + PSEUDO: function( elem, match, i, array ) { + var name = match[1], + filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0; + + } else if ( name === "not" ) { + var not = match[3]; + + for ( var j = 0, l = not.length; j < l; j++ ) { + if ( not[j] === elem ) { + return false; + } + } + + return true; + + } else { + Sizzle.error( name ); + } + }, + + CHILD: function( elem, match ) { + var first, last, + doneName, parent, cache, + count, diff, + type = match[1], + node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + /* falls through */ + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + + case "nth": + first = match[2]; + last = match[3]; + + if ( first === 1 && last === 0 ) { + return true; + } + + doneName = match[0]; + parent = elem.parentNode; + + if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) { + count = 0; + + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + + parent[ expando ] = doneName; + } + + diff = elem.nodeIndex - last; + + if ( first === 0 ) { + return diff === 0; + + } else { + return ( diff % first === 0 && diff / first >= 0 ); + } + } + }, + + ID: function( elem, match ) { + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + + TAG: function( elem, match ) { + return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match; + }, + + CLASS: function( elem, match ) { + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + + ATTR: function( elem, match ) { + var name = match[1], + result = Sizzle.attr ? + Sizzle.attr( elem, name ) : + Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + !type && Sizzle.attr ? + result != null : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value !== check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + + POS: function( elem, match, i, array ) { + var name = match[2], + filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS, + fescape = function(all, num){ + return "\\" + (num - 0 + 1); + }; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); +} +// Expose origPOS +// "global" as in regardless of relation to brackets/parens +Expr.match.globalPOS = origPOS; + +var makeArray = function( array, results ) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +// Perform a simple check to determine if the browser is capable of +// converting a NodeList to an array using builtin methods. +// Also verifies that the returned array holds DOM nodes +// (which is not the case in the Blackberry browser) +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; + +// Provide a fallback method if it does not work +} catch( e ) { + makeArray = function( array, results ) { + var i = 0, + ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + + } else { + if ( typeof array.length === "number" ) { + for ( var l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + + } else { + for ( ; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder, siblingCheck; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + return a.compareDocumentPosition ? -1 : 1; + } + + return a.compareDocumentPosition(b) & 4 ? -1 : 1; + }; + +} else { + sortOrder = function( a, b ) { + // The nodes are identical, we can exit early + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Fallback to using sourceIndex (in IE) if it's available on both nodes + } else if ( a.sourceIndex && b.sourceIndex ) { + return a.sourceIndex - b.sourceIndex; + } + + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + // If the nodes are siblings (or identical) we can do a quick check + if ( aup === bup ) { + return siblingCheck( a, b ); + + // If no parents were found then the nodes are disconnected + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + // Otherwise they're somewhere else in the tree so we need + // to build up a full list of the parentNodes for comparison + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + // Start walking down the tree looking for a discrepancy + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + // We ended someplace up the tree so do a sibling check + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + + siblingCheck = function( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; + }; +} + +// Check to see if the browser returns elements by name when +// querying by getElementById (and provide a workaround) +(function(){ + // We're going to inject a fake input element with a specified name + var form = document.createElement("div"), + id = "script" + (new Date()).getTime(), + root = document.documentElement; + + form.innerHTML = "
      "; + + // Inject it into the root element, check its status, and remove it quickly + root.insertBefore( form, root.firstChild ); + + // The workaround has to do additional checks after a getElementById + // Which slows things down for other browsers (hence the branching) + if ( document.getElementById( id ) ) { + Expr.find.ID = function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + + return m ? + m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? + [m] : + undefined : + []; + } + }; + + Expr.filter.ID = function( elem, match ) { + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + + // release memory in IE + root = form = null; +})(); + +(function(){ + // Check to see if the browser returns only elements + // when doing getElementsByTagName("*") + + // Create a fake element + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + // Make sure no comments are found + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function( match, context ) { + var results = context.getElementsByTagName( match[1] ); + + // Filter out possible comments + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + // Check to see if an attribute returns normalized href attributes + div.innerHTML = ""; + + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + + Expr.attrHandle.href = function( elem ) { + return elem.getAttribute( "href", 2 ); + }; + } + + // release memory in IE + div = null; +})(); + +if ( document.querySelectorAll ) { + (function(){ + var oldSizzle = Sizzle, + div = document.createElement("div"), + id = "__sizzle__"; + + div.innerHTML = "

      "; + + // Safari can't handle uppercase or unicode characters when + // in quirks mode. + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function( query, context, extra, seed ) { + context = context || document; + + // Only use querySelectorAll on non-XML documents + // (ID selectors don't work in non-HTML documents) + if ( !seed && !Sizzle.isXML(context) ) { + // See if we find a selector to speed up + var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); + + if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { + // Speed-up: Sizzle("TAG") + if ( match[1] ) { + return makeArray( context.getElementsByTagName( query ), extra ); + + // Speed-up: Sizzle(".CLASS") + } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { + return makeArray( context.getElementsByClassName( match[2] ), extra ); + } + } + + if ( context.nodeType === 9 ) { + // Speed-up: Sizzle("body") + // The body element only exists once, optimize finding it + if ( query === "body" && context.body ) { + return makeArray( [ context.body ], extra ); + + // Speed-up: Sizzle("#ID") + } else if ( match && match[3] ) { + var elem = context.getElementById( match[3] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id === match[3] ) { + return makeArray( [ elem ], extra ); + } + + } else { + return makeArray( [], extra ); + } + } + + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(qsaError) {} + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + var oldContext = context, + old = context.getAttribute( "id" ), + nid = old || id, + hasParent = context.parentNode, + relativeHierarchySelector = /^\s*[+~]/.test( query ); + + if ( !old ) { + context.setAttribute( "id", nid ); + } else { + nid = nid.replace( /'/g, "\\$&" ); + } + if ( relativeHierarchySelector && hasParent ) { + context = context.parentNode; + } + + try { + if ( !relativeHierarchySelector || hasParent ) { + return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); + } + + } catch(pseudoError) { + } finally { + if ( !old ) { + oldContext.removeAttribute( "id" ); + } + } + } + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + // release memory in IE + div = null; + })(); +} + +(function(){ + var html = document.documentElement, + matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; + + if ( matches ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9 fails this) + var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), + pseudoWorks = false; + + try { + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( document.documentElement, "[test!='']:sizzle" ); + + } catch( pseudoError ) { + pseudoWorks = true; + } + + Sizzle.matchesSelector = function( node, expr ) { + // Make sure that attribute selectors are quoted + expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); + + if ( !Sizzle.isXML( node ) ) { + try { + if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { + var ret = matches.call( node, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || !disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9, so check for that + node.document && node.document.nodeType !== 11 ) { + return ret; + } + } + } catch(e) {} + } + + return Sizzle(expr, null, null, [node]).length > 0; + }; + } +})(); + +(function(){ + var div = document.createElement("div"); + + div.innerHTML = "
      "; + + // Opera can't find a second classname (in 9.6) + // Also, make sure that getElementsByClassName actually exists + if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { + return; + } + + // Safari caches class attributes, doesn't catch changes (in 3.2) + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) { + return; + } + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function( match, context, isXML ) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + // release memory in IE + div = null; +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem[ expando ] === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem[ expando ] = doneName; + elem.sizset = i; + } + + if ( elem.nodeName.toLowerCase() === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem[ expando ] === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem[ expando ] = doneName; + elem.sizset = i; + } + + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +if ( document.documentElement.contains ) { + Sizzle.contains = function( a, b ) { + return a !== b && (a.contains ? a.contains(b) : true); + }; + +} else if ( document.documentElement.compareDocumentPosition ) { + Sizzle.contains = function( a, b ) { + return !!(a.compareDocumentPosition(b) & 16); + }; + +} else { + Sizzle.contains = function() { + return false; + }; +} + +Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; + + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +var posProcess = function( selector, context, seed ) { + var match, + tmpSet = [], + later = "", + root = context.nodeType ? [context] : context; + + // Position selectors must be done after the filter + // And so must :not(positional) so we move all PSEUDOs to the end + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet, seed ); + } + + return Sizzle.filter( later, tmpSet ); +}; + +// EXPOSE +// Override sizzle attribute retrieval +Sizzle.attr = jQuery.attr; +Sizzle.selectors.attrMap = {}; +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.filters; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})(); + + +var runtil = /Until$/, + rparentsprev = /^(?:parents|prevUntil|prevAll)/, + // Note: This RegExp should be improved, or likely pulled from Sizzle + rmultiselector = /,/, + isSimple = /^.[^:#\[\.,]*$/, + slice = Array.prototype.slice, + POS = jQuery.expr.match.globalPOS, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var self = this, + i, l; + + if ( typeof selector !== "string" ) { + return jQuery( selector ).filter(function() { + for ( i = 0, l = self.length; i < l; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }); + } + + var ret = this.pushStack( "", "find", selector ), + length, n, r; + + for ( i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + jQuery.find( selector, this[i], ret ); + + if ( i > 0 ) { + // Make sure that the results are unique + for ( n = length; n < ret.length; n++ ) { + for ( r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return ret; + }, + + has: function( target ) { + var targets = jQuery( target ); + return this.filter(function() { + for ( var i = 0, l = targets.length; i < l; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false), "not", selector); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }, + + is: function( selector ) { + return !!selector && ( + typeof selector === "string" ? + // If this is a positional selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + POS.test( selector ) ? + jQuery( selector, this.context ).index( this[0] ) >= 0 : + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }, + + closest: function( selectors, context ) { + var ret = [], i, l, cur = this[0]; + + // Array (deprecated as of jQuery 1.7) + if ( jQuery.isArray( selectors ) ) { + var level = 1; + + while ( cur && cur.ownerDocument && cur !== context ) { + for ( i = 0; i < selectors.length; i++ ) { + + if ( jQuery( cur ).is( selectors[ i ] ) ) { + ret.push({ selector: selectors[ i ], elem: cur, level: level }); + } + } + + cur = cur.parentNode; + level++; + } + + return ret; + } + + // String + var pos = POS.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( i = 0, l = this.length; i < l; i++ ) { + cur = this[i]; + + while ( cur ) { + if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { + ret.push( cur ); + break; + + } else { + cur = cur.parentNode; + if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) { + break; + } + } + } + } + + ret = ret.length > 1 ? jQuery.unique( ret ) : ret; + + return this.pushStack( ret, "closest", selectors ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? + all : + jQuery.unique( all ) ); + }, + + andSelf: function() { + return this.add( this.prevObject ); + } +}); + +// A painfully simple check to see if an element is disconnected +// from a document (should be improved, where feasible). +function isDisconnected( node ) { + return !node || !node.parentNode || node.parentNode.nodeType === 11; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return jQuery.nth( elem, 2, "nextSibling" ); + }, + prev: function( elem ) { + return jQuery.nth( elem, 2, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.makeArray( elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; + + if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret, name, slice.call( arguments ).join(",") ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 ? + jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : + jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + nth: function( cur, result, dir, elem ) { + result = result || 1; + var num = 0; + + for ( ; cur; cur = cur[dir] ) { + if ( cur.nodeType === 1 && ++num === result ) { + break; + } + } + + return cur; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, keep ) { + + // Can't pass null or undefined to indexOf in Firefox 4 + // Set to 0 to skip string check + qualifier = qualifier || 0; + + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + var retVal = !!qualifier.call( elem, i, elem ); + return retVal === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem, i ) { + return ( elem === qualifier ) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem, i ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; + }); +} + + + + +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, + rtagName = /<([\w:]+)/, + rtbody = /]", "i"), + // checked="checked" or checked + rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, + rscriptType = /\/(java|ecma)script/i, + rcleanScript = /^\s*", "" ], + legend: [ 1, "
      ", "
      " ], + thead: [ 1, "", "
      " ], + tr: [ 2, "", "
      " ], + td: [ 3, "", "
      " ], + col: [ 2, "", "
      " ], + area: [ 1, "", "" ], + _default: [ 0, "", "" ] + }, + safeFragment = createSafeFragment( document ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// IE can't serialize and + - + diff --git a/feeds/webclipfeed.php b/feeds/webclipfeed.php index 5f8b488..a3d749e 100644 --- a/feeds/webclipfeed.php +++ b/feeds/webclipfeed.php @@ -13,7 +13,7 @@
      KKnowledge ArticlesKKnowledge
      -
      +
      - +
      Klips FeedLatest Klips
      diff --git a/grid.php b/grid.php new file mode 100644 index 0000000..a362f10 --- /dev/null +++ b/grid.php @@ -0,0 +1,60 @@ + + + + + + + + +
      +
        +
        + + + + + + + + + + + diff --git a/links/jdownloader.php b/links/jdownloader.php index 67e02e4..30c117e 100644 --- a/links/jdownloader.php +++ b/links/jdownloader.php @@ -1,9 +1,23 @@ "; + echo "
      • "; } else { - echo ""; +echo "
      • "; + } -?> \ No newline at end of file +?> + + + + + + + + + + + + + diff --git a/links/transmission.php b/links/transmission.php index 608371f..151c3e5 100644 --- a/links/transmission.php +++ b/links/transmission.php @@ -1,5 +1,6 @@ "; + echo "
      • "; } -?> \ No newline at end of file +?> + diff --git a/links/utorrent.php b/links/utorrent.php index b25a28c..c2691bf 100644 --- a/links/utorrent.php +++ b/links/utorrent.php @@ -1,9 +1,10 @@ "; + echo "
      • "; } else { - echo ""; + echo "
      • "; } -?> \ No newline at end of file +?> + diff --git a/newtest.php b/newtest.php new file mode 100644 index 0000000..561826d --- /dev/null +++ b/newtest.php @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        +
        + + + + +
        +$yourname's Server Access

        ";?> +
        + +

        +"; ?> +

        +
        + +
        +
          + + "0") { + require "links/jdownloader.php"; + } +?> + "0") { + require "links/torrent.php"; + } +?> + +
        • A
        • +
        • A
        • +
        • A
        • +
        • F
        • + +
        • + +
        +
        + +
        + +
        +

        +Adults"; ?> - Kids"; ?> +

        +
        +
        +
        +"; ?> +"; ?> +"; ?> +"; ?> +"; ?> +"; ?> +"; ?> +"; ?> +"; ?> +"; ?> +"; ?> + + + +
        + + + + + \ No newline at end of file diff --git a/right.php b/right.php index d1b7773..cca70d7 100644 --- a/right.php +++ b/right.php @@ -105,7 +105,7 @@ if ($customfeed3visible > "0") { - + diff --git a/robots.txt b/robots.txt new file mode 100644 index 0000000..f783256 --- /dev/null +++ b/robots.txt @@ -0,0 +1,5 @@ + +# This file can be used to affect how search engines and other web site crawlers see your site. +# For more information, please see http://www.w3.org/TR/html4/appendix/notes.html#h-B.4.1.1 +# WebMatrix 2.0 +
        ThemeTheme