diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 00000000..1da5e27a --- /dev/null +++ b/.jshintrc @@ -0,0 +1,22 @@ +{ + "browser": true, + "curly": true, + "evil": true, + "globals": { + "console": true, + "define": true, "exports": true, "require": true, "module": true, + "describe": true, "xdescribe": true, "it": true, "xit": true, "expect": true, "runs": true, "waits": true, "waitsFor": true, "itConditionally": true, + "easyXDM": true, + "EventEmitter2": true, + "F2": true, + "jQuery": true, + "$": true + }, + "latedef": true, + "noarg": true, + "quotmark": "single", + "shadow": false, + "sub": true, + "undef": true, + "unused": "vars" +} \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 774fb676..ff95b535 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,9 @@ -language: ruby -rvm: - - 1.9.3 +language: node_js +node_js: + - "0.10" notifications: email: false before_script: - - "export DISPLAY=:99.0" - - "sh -e /etc/init.d/xvfb start" + - npm install grunt-cli script: - - "wget https://raw.github.com/mark-rushakoff/OpenPhantomScripts/master/phantom-jasmine.js" - - "phantomjs phantom-jasmine.js tests/index.html" - - "phantomjs phantom-jasmine.js tests/index-amd.html" \ No newline at end of file + - grunt travis \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 68d804e6..8ede4ca5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,7 @@ A couple of ground rules. * Create a branch from where you want to base your changes. * Do not work directly in `master`; create a branch _based on_ `master` using `git checkout -b 'your_branch_name' master`. -* Follow our [coding standards](https://github.com/OpenF2/F2/wiki/Contributing-to-F2). +* Follow our [coding standards](https://github.com/OpenF2/F2/wiki/Coding-Standards). * Preferably add _and document_ unit test(s) for your changes. * Re-run all the Jasmine tests to confirm your changes didn't break anything. * Perform browser testing in our [supported browsers](wiki/Browser-Compatibility). @@ -38,7 +38,7 @@ Coding standards promote a common vocabulary and syntax so that our fellow devel Coding standards help an individual project, and also make it easier for one project to reuse components from another project. This, of course, is a core component of F2. -Read our [coding standards](https://github.com/OpenF2/F2/wiki/Contributing-to-F2) on the wiki. +Read our [coding standards](https://github.com/OpenF2/F2/wiki/Coding-Standards) on the wiki. ## Keep in Touch diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 00000000..ca8c6e74 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,394 @@ +module.exports = function(grunt) { + + var handlebars = require('handlebars'), + moment = require('moment'), + pkg = grunt.file.readJSON('package.json'), + semver = require('semver'); + + // TODO: Remove Handlebars dependency and use the built-in grunt templating + // Handlebars helpers + handlebars.registerHelper('if', function(conditional, options) { + if (options.hash.desired === options.hash.test) { + return options.fn(this); + } + }); + + // Project config + grunt.initConfig({ + pkg: pkg, + clean: { + docs: ['docs/src-temp'], + 'github-pages': { + options: { force: true }, + src: ['../gh-pages/src'] + }, + 'F2-examples': { + options: { force: true }, + src: ['./F2-examples.zip'] + } + }, + copy: { + docs: { + files: [ + { + expand: true, + cwd: 'docs/src/', + src: ['**'], + dest: 'docs/src-temp/', + filter: function(src) { + if ( !(/twbootstrap/).test(src) ){//don't touch submodule + return (/(.html|.md)$/i).test(src); + } + } + } + ], + options: { + processContent: function(content, srcpath) { + // TODO: Remove Handlebars dependency and use the built-in grunt + // templating compile and run the Handlebars template + return (handlebars.compile(content))(pkg); + } + } + }, + 'github-pages': { + files: [ + { + expand: true, + cwd: 'docs/', + src: ['**'], + dest: '../gh-pages' + } + ] + }, + 'F2-examples': { + files: [ + { + expand: true, + cwd: './', + src: ['F2-examples.zip'], + dest: '../gh-pages' + } + ] + } + }, + compress: { + main: { + options: { + archive: 'F2-examples.zip', + pretty: true + }, + files: [ + { + expand: true, + cwd: 'examples/', + src: ['**'], + dest: '../F2-examples' + } + ] + } + }, + concat: { + options: { + process: { data: pkg }, + separator: '\n', + stripBanners: false + }, + 'no-third-party': { + src: [ + 'sdk/src/template/header.js.tmpl', + '<%= jshint.files %>', + 'sdk/src/template/footer.js.tmpl' + ], + dest: 'sdk/f2.no-third-party.js' + }, + dist: { + src: [ + 'sdk/src/template/header.js.tmpl', + 'sdk/src/third-party/json2.js', + 'sdk/src/third-party/jquery.js', + 'sdk/src/third-party/bootstrap-modal.js', + 'sdk/src/third-party/jquery.noconflict.js', + 'sdk/src/third-party/eventemitter2.js', + 'sdk/src/third-party/easyXDM/easyXDM.js', + '<%= jshint.files %>', + 'sdk/src/template/footer.js.tmpl' + ], + dest: 'sdk/f2.debug.js' + } + }, + /** + * Need to downgrade forever-monitor to v1.1 because of: + * https://github.com/blai/grunt-express/issues/12 + * cd node_modules/grunt-express; npm uninstall forever-monitor; npm install forever-monitor@1.1; + */ + express: { + server: { + options: { + bases: './', + port: 8080, + server: (require('path')).resolve('./tests/server') + } + } + }, + jasmine: { + 'non-amd': { + options: { + host: 'http://localhost:8080/tests/', + outfile: 'index.html' + } + }, + 'amd': { + options: { + host: 'http://localhost:8080/tests/', + outfile: 'index-amd.html' + } + } + }, + jshint: { + options: { + jshintrc: '.jshintrc' + }, + files: [ + 'sdk/src/F2.js', + 'sdk/src/app_handlers.js', + 'sdk/src/classes.js', + 'sdk/src/constants.js', + 'sdk/src/events.js', + 'sdk/src/rpc.js', + 'sdk/src/ui.js', + 'sdk/src/container.js' + ] + }, + less: { + dist: { + options: { + compress: true + }, + files: { + './docs/css/F2.css': './docs/src/template/less/bootstrap.less', + './docs/css/F2.Docs.css': './docs/src/template/less/bootstrap-docs.less', + './docs/css/F2.Sdk.css': './docs/src/template/less/bootstrap-sdk.less' + } + } + }, + uglify: { + options: { + preserveComments: function(node, comment) { + return (/^!/).test(comment.value); + } + }, + dist: { + files: {'sdk/f2.min.js' : ['sdk/f2.debug.js']}, + options: { + report: 'gzip' + } + }, + sourcemap: { + files: '<%= uglify.dist.files %>', + options: { + sourceMap: function(fileName) { + return fileName.replace(/\.js$/, '.map'); + }, + sourceMapPrefix: 1, + sourceMappingURL: function(path) { + return path.replace(grunt.config('sourcemap.options.prefix'), '').replace(/\.js$/, '.map'); + } + } + } + }, + sourcemap: { + options: { + src: 'sdk/f2.min.js', + prefix: 'sdk/' + } + } + }); + + // Load plugins + grunt.loadNpmTasks('grunt-contrib-clean'); + grunt.loadNpmTasks('grunt-contrib-copy'); + grunt.loadNpmTasks('grunt-contrib-concat'); + grunt.loadNpmTasks('grunt-contrib-compress'); + grunt.loadNpmTasks('grunt-contrib-jasmine'); + grunt.loadNpmTasks('grunt-contrib-jshint'); + grunt.loadNpmTasks('grunt-contrib-less'); + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-express'); + + // Register tasks + grunt.registerTask('fix-sourcemap', 'Fixes the source map file', function() { + var uglifyOptions = grunt.config('uglify.sourcemap.options'), + options = grunt.config('sourcemap.options'), + dest = uglifyOptions.sourceMap(options.src), + rawMap = grunt.file.read(dest); + + rawMap = rawMap.replace(options.prefix, ''); + grunt.file.write(dest, rawMap); + }); + + grunt.registerTask('markitdown', 'Compiles the spec documentation with Markitdown', function() { + var done = this.async(), + log = grunt.log.write('Generating spec documentation...'); + grunt.util.spawn( + { + cmd: 'markitdown', + args: [ + './', + '--output-path', '../', + '--docTemplate', './template/baseTemplate.html', + '--header', './template/header.html', + '--footer', './template/footer.html', + '--head', './template/style.html', + '--title', 'F2' + ], + opts: { + cwd: './docs/src-temp' + } + }, + function(error, result, code) { + if (error) { + grunt.fail.fatal(error); + } else { + log.ok(); + done(); + } + } + ); + }); + + grunt.registerTask('nuget', 'Builds the NuGet package for distribution on NuGet.org', function() { + var done = this.async(), + log = grunt.log.write('Creating NuSpec file...'), + nuspec = grunt.file.read('./sdk/f2.nuspec.tmpl'); + + nuspec = grunt.template.process(nuspec, { data: pkg }); + grunt.file.write('./sdk/f2.nuspec', nuspec); + log.ok(); + + log = grunt.log.write('Creating NuGet package...'); + grunt.util.spawn( + { + cmd: 'nuget', + args: ['pack', 'f2.nuspec'], + opts: { + cwd: './sdk' + } + }, + function(error, result, code){ + if (error){ + grunt.fail.fatal(error); + } else { + grunt.file.delete('./sdk/f2.nuspec'); + log.ok(); + done(); + } + } + ); + }); + + grunt.registerTask('release', 'Prepares the code for release (merge into master)', function(releaseType) { + if (!/^major|minor|patch$/i.test(releaseType) && !semver.valid(releaseType)) { + grunt.log.error('"' + releaseType + '" is not a valid release type (major, minor, or patch) or SemVer version'); + return; + } + + pkg.version = semver.valid(releaseType) ? releaseType : String(semver.inc(pkg.version, releaseType)).replace(/\-\w+$/, ''); + pkg._releaseDate = new Date().toJSON(); + pkg._releaseDateFormatted = moment(pkg._releaseDate).format('D MMMM YYYY'); + + grunt.file.write('./package.json', JSON.stringify(pkg, null, '\t')); + grunt.config.set('pkg', pkg); + + grunt.task.run('version'); + }); + + grunt.registerTask('version', 'Displays version information for F2', function() { + grunt.log.writeln(grunt.template.process( + 'This copy of F2 is at version <%= version %> with a release date of <%= _releaseDateFormatted %>', + { data: pkg } + )); + }); + + grunt.registerTask('yuidoc', 'Builds the reference documentation with YUIDoc', function() { + + var builder, + docOptions = { + quiet: true, + norecurse: true, + paths: ['./sdk/src'], + outdir: './docs/sdk/', + themedir: './docs/src/sdk-template' + }, + done = this.async(), + json, + log = grunt.log.write('Generating reference documentation...'), + readmeMd = grunt.file.read('README.md'), + Y = require('yuidocjs'); + + json = (new Y.YUIDoc(docOptions)).run(); + // massage in some meta information from F2.json + json.project = { + docsAssets: '../', + version: pkg.version, + releaseDateFormatted: pkg._releaseDateFormatted + }; + docOptions = Y.Project.mix(json, docOptions); + + // hasClassMembers + // ensures that the class has members and isn't just an empty namespace + Y.Handlebars.registerHelper('hasClassMembers', function() { + + for (var i = 0, len = json.classitems.length; i < len; i++) { + //console.log(json.classitems[i].class, this.name); + if (json.classitems[i].class === this.name) { + return ''; + } + } + + return 'hide'; + }); + + // title tag + Y.Handlebars.registerHelper('htmlTitle',function () { + var name = this.displayName || this.name, + title = name; + + if (title) { + title = 'F2 - ' + title; + } else { + title = 'F2 - The Open Financial Framework'; + } + + return title; + }); + + // handle any member names that have periods in them + Y.Handlebars.registerHelper('memberNameAsId', function() { + return String(this.name).replace('.', '_'); + }); + + // insert readme markdown + Y.Handlebars.registerHelper('readme', function() { + return builder.markdown(readmeMd, true); + }); + + builder = new Y.DocBuilder(docOptions, json); + builder.compile(function() { + log.ok(); + done(); + }); + }); + + + + grunt.registerTask('docs', ['less', 'yuidoc', 'copy:docs', 'markitdown', 'clean:docs']); + grunt.registerTask('github-pages', ['copy:github-pages', 'clean:github-pages']); + grunt.registerTask('zip', ['compress', 'copy:F2-examples', 'clean:F2-examples']); + grunt.registerTask('js', ['jshint', 'concat', 'uglify:dist', 'sourcemap']); + grunt.registerTask('sourcemap', ['uglify:sourcemap', 'fix-sourcemap']); + grunt.registerTask('test', ['jshint', 'express', 'jasmine'/*, 'express-keepalive'*/]); + grunt.registerTask('travis', ['test']); + + // the default task + grunt.registerTask('default', ['test', 'js', 'docs', 'zip']); +}; \ No newline at end of file diff --git a/LICENSE b/LICENSE index 7cf0b719..7a4a3ea2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,22 +1,202 @@ -MIT License. - -Copyright (c) 2013 Markit On Demand, Inc. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in 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. \ No newline at end of file + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/README.md b/README.md index a8d48012..3ad40d4f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ -# An open framework for the financial services industry. +# F2 - An open web framework for the financial services industry. + F2 is an open and free web integration framework designed to help you and other financial industry participants develop custom solutions that combine the best tools and content from multiple providers into one, privately-labeled, seamlessly integrated front-end. The [essential components](http://docs.openf2.org/index.html#framework) defined by the F2 specification are the Container, Apps, Context and Store—all supported under the hood by **[F2.js](http://docs.openf2.org/f2js-sdk.html)**, a JavaScript SDK which provides an extensible foundation powering all F2-based web applications. F2 is currently maintained by [Markit On Demand](http://www.markitondemand.com) and you're encouraged to read [more details about the management of the F2 spec](http://docs.openf2.org/#spec-management). Visit [OpenF2.org](http://www.openf2.org) for more information and follow [@OpenF2](http://twitter.com/OpenF2) on Twitter. @@ -10,7 +11,7 @@ Clone the repo, `git clone https://github.com/OpenF2/F2.git`, or [download the l Now you've got F2, you are ready to start building F2 containers or apps. Read the [Get Started documentation](http://docs.openf2.org/app-development.html) for F2 apps to begin. If you simply want to see examples, point your browser at `http://path/to/your/F2/examples/`. -**Important**: If you simply want to build F2 [containers](http://docs.openf2.org/container-development.html) or [apps](http://docs.openf2.org/app-development.html), you can **skip** the [Build F2](#build-f2) section below. You do not need the command line to work with F2. +**Important**: If you simply want to build F2 [containers](http://docs.openf2.org/container-development.html) or [apps](http://docs.openf2.org/app-development.html), you can **skip** the [Build F2](#build-f2-) section below. You do not need the command line to work with F2. ## Versioning @@ -24,7 +25,7 @@ For more information on SemVer, please visit . You can run this command to check the version of your local copy of F2 (if you've [setup the build](#developers)): -`$> node build -v` +`$> grunt version` ## Talk @@ -38,25 +39,29 @@ To track bugs and issues, we are using [Issues on GitHub](https://github.com/Ope If you just want to build F2 [containers](http://docs.openf2.org/container-development.html) and/or [apps](http://docs.openf2.org/app-development.html), you can **skip the [Build F2](#build-f2)** section below. You do not need the command line to work with F2. -### Build F2 [![Build Status](https://travis-ci.org/OpenF2/F2.png)](https://travis-ci.org/OpenF2/F2) +### Build F2 [![Build Status](https://travis-ci.org/OpenF2/F2.png?branch=master)](https://travis-ci.org/OpenF2/F2) -For those wishing to contribute back to F2, we've included a `build` file in the project which contains the logic for compiling F2.js and the specification docs. The build script runs on [Node.js](http://nodejs.org/) and has a few dependencies. To install, `cd` to your `F2/build` folder, and run the following commands in npm: +For those wishing to [contribute back to F2](CONTRIBUTING.md), we've included a `Gruntfile` for use with [Grunt](http://gruntjs.com/) which contains the logic for compiling and testing F2.js and the specification docs. Grunt is built on top of [Node.js](http://nodejs.org/) and is installed via [npm](https://npmjs.org/). -`$> npm install uglify-js@1.3.4 wrench fs-extra yuidocjs optimist handlebars` +To configure your environment, be sure you have Node installed and run the following command from the project root directory: -`$> npm install less markitdown -g` +`$> npm install` -Depending on your configuration, you may need to be an admin to install some of these Node packages. Additionally, some packages may need to be [installed globally](http://blog.nodejs.org/2011/03/23/npm-1-0-global-vs-local-installation/) using the `-g` switch. +This command will install the [Grunt CLI](http://gruntjs.com/getting-started#installing-the-cli) in addition to all dependencies needed to run the `Gruntfile`. Depending on your configuration, you may need to be an admin to install some of these Node packages. Additionally, some packages may need to be [installed globally](http://blog.nodejs.org/2011/03/23/npm-1-0-global-vs-local-installation/) using the `-g` switch. If the installation fails due to directory permissions, use: + +`$> sudo npm install` + +We are using [markitdown](https://github.com/markitondemand/markitdown), a lightweight pandoc wrapper, for converting markdown files to HTML for the [F2 docs](http://docs.openf2.org). [Pandoc](http://johnmacfarlane.net/pandoc/index.html) is required for markitdown and there are [installation packages available](http://johnmacfarlane.net/pandoc/installing.html) for Windows and Mac OS. To **build F2**, run: -`$> node build` +`$> grunt` For help, run: -`$> node build -h` +`$> grunt --help` -We are using [markitdown](https://github.com/markitondemand/markitdown), a lightweight pandoc wrapper, for converting markdown files to HTML for the [docs](http://docs.openf2.org). +Some Mac users have run into a Grunt "cannot run in wd" error when using `grunt` and/or `npm install`. If you're getting that error in your shell, try using `--unsafe-perm` [as discussed here](https://github.com/isaacs/npm/issues/2984). #### NuGet Package @@ -70,12 +75,13 @@ Join our team and help contribute to F2 on GitHub. Begin by reading our [contrib ## Copyright and License -F2 is released under the MIT License. - Copyright © 2013 Markit On Demand, Inc. -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in 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: +"F2" is licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: + +[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +Please note that F2 ("Software") may contain third party material that Markit On Demand Inc. has a license to use and include within the Software (the "Third Party Material"). A list of the software comprising the Third Party Material and the terms and conditions under which such Third Party Material is distributed are reproduced in the [ThirdPartyMaterial.md](ThirdPartyMaterial.md) file. The inclusion of the Third Party Material in the Software does not grant, provide nor result in you having acquiring any rights whatsoever, other than as stipulated in the terms and conditions related to the specific Third Party Material, if any. -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/ThirdPartyMaterial.md b/ThirdPartyMaterial.md new file mode 100644 index 00000000..9fd611ed --- /dev/null +++ b/ThirdPartyMaterial.md @@ -0,0 +1,70 @@ +# F2 - Third Party Material + +The following are the terms and conditions governing your use of the third party material that Markit On Demand Inc. has a license to use and include within F2: + +## json2 + +JSON.org requires the following notice to accompany json2: + +Copyright © 2002 JSON.org + +[http://json.org](json.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +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. + +## jQuery + +The jQuery Foundation and other contributors require the following notice to accompany jQuery: + +Copyright © 2013 jQuery Foundation and other contributors + +[http://jquery.com/](http://jquery.com/) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +## Bootstrap + +Twitter, Inc. require the following notice to accompany Bootstrap: + +Copyright © 2012 Twitter, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with the License. You may obtain a copy of the License in the LICENSE file, or at: + +[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +## EventEmitter + +Hij1nx requires the following notice to accompany EventEmitter: + +Copyright © 2011 hij1nx + +[http://www.twitter.com/hij1nx](http://www.twitter.com/hij1nx) + +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. + +## easyXDM + +Øyvind Sean Kinsey and others require the following notice to accompany easyXDM: + +Copyright © 2009-2011 Øyvind Sean Kinsey, [oyvind@kinsey.no](mailto:oyvind@kinsey.no) + +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/build/F2.json b/build/F2.json deleted file mode 100644 index 615e90ac..00000000 --- a/build/F2.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "docs": { - "version": "1.1.2", - "shortVersion": "1.1.2", - "releaseDate": "2013-04-08T14:34:33.095Z", - "lastUpdateDate": "2013-04-08T14:34:35.678Z", - "lastUpdateDateFormatted": "8 April 2013" - }, - "sdk": { - "version": "1.1.2", - "shortVersion": "1.1", - "releaseDate": "2013-04-08T14:34:33.095Z", - "lastUpdateDate": "2013-04-08T14:34:33.926Z" - }, - "branch": "master" -} \ No newline at end of file diff --git a/build/README.md b/build/README.md deleted file mode 100644 index 9793d7c4..00000000 --- a/build/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# Build F2 [![Build Status](https://travis-ci.org/OpenF2/F2.png)](https://travis-ci.org/OpenF2/F2) - -For those wishing to contribute back to F2, we've included a `build` file in the project which contains the logic for compiling F2.js and the specification docs. The build script runs on [Node.js](http://nodejs.org/) and has a few dependencies. To install, `cd` to your `F2/build` folder, and run the following commands in npm: - -`$> npm install uglify-js@1.3.4 wrench fs-extra yuidocjs optimist handlebars` - -`$> npm install less markitdown -g` - -Depending on your configuration, you may need to be an admin to install some of these Node packages. Additionally, some packages may need to be [installed globally](http://blog.nodejs.org/2011/03/23/npm-1-0-global-vs-local-installation/) using the `-g` switch. - -To **build F2**, run: - -`$> node build` - -For help, run: - -`$> node build -h` - -Before you begin coding, familiarize yourself with our [contribution guidelines](CONTRIBUTING.md), and then start by [forking the repo](https://github.com/OpenF2/F2/fork_select), sending [pull requests](https://help.github.com/articles/using-pull-requests), or [submitting issues](https://github.com/OpenF2/F2/issues). - -## NuGet Package - -Good news if you're using C#! We have an [F2 NuGet package available](https://nuget.org/packages/F2/). In the Package Manager Console run: - -`PM> Install-Package F2` - -## Versioning - -To adhere to industry standards, F2 will be maintained under the Semantic Versioning guidelines as much as possible. - -Releases will be numbered with the following format: - -`..` - -For more information on SemVer, please visit . - -You can run this command to check the version of your local copy of F2: - -`$> node build -v` - -## Talk - -Have a question? Want to talk? Ask it on our [Google Group](https://groups.google.com/forum/#!forum/OpenF2) or send an email to . - -## Bug Tracking - -To track bugs and issues, we are using [Issues on GitHub](https://github.com/OpenF2/F2/issues). - -## Copyright and License - -F2 is released under the MIT License. - -Copyright © 2013 Markit On Demand, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in 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/build/build.js b/build/build.js deleted file mode 100755 index 108187cd..00000000 --- a/build/build.js +++ /dev/null @@ -1,707 +0,0 @@ -#!/usr/bin/env node -/** - * Build script for F2 - * - * This script requires that the following node packages be installed: - * - markitdown (npm install -g markitdown) - * - optimist (npm install optimist) - * - uglify-js (npm install uglify-js) - * - wrench-js (npm install wrench) - * - yuidocjs (npm install yuidocjs) - * also requires pandoc: http://johnmacfarlane.net/pandoc/installing.html - */ -// change directory to root folder for simplicity -process.chdir('../'); - -var exec = require('child_process').exec; -var fs = require('fs-extra'); -var handlebars = require('Handlebars'); -var jsp = require('uglify-js').parser; -var optimist = require('optimist'); -var pro = require('uglify-js').uglify; -var f2Info = require('./F2.json'); -var wrench = require('wrench'); -var Y = require('yuidocjs'); -var argv = optimist - .usage('Build script for F2\nUsage: $0 [options]') - .boolean('a').alias('a', 'all').describe('a', 'Build all') - .boolean('d').alias('d', 'docs').describe('d', 'Build the docs') - .boolean('l').alias('l', 'less').describe('l', 'Compile LESS') - .boolean('g').alias('g', 'gh-pages').describe('g', 'Copy docs to gh-pages folder. Must have the gh-pages branch cloned to ../gh-pages') - .boolean('h').alias('h', 'help').describe('h', 'Display this help information') - .boolean('j').alias('j', 'js-sdk').describe('j', 'Build just the JS SDK') - .boolean('n').alias('n', 'nuget').describe('n', 'Build just the Nuget Package') - .boolean('v').alias('v', 'version').describe('v', 'Output the verison information for F2') - .boolean('y').alias('y', 'yuidoc').describe('y', 'Build the YUIDoc for the SDK') - .string('prep').describe('prep', 'Updates F2Info, does \'build -a\' including rebuild of templates') - .string('release').describe('release', 'Updates the sdk release version in F2.json and creates a tag on GitHub') - .string('release-docs').describe('release-docs', 'Update the docs release version in F2.json') - .string('release-sdk').describe('release-sdk', 'Update the sdk release version in F2.json') - .argv; - -// constants -var JS_HEADER = { src: 'sdk/src/template/header.js.tmpl', minify: false }; -var JS_FOOTER = { src: 'sdk/src/template/footer.js.tmpl', minify: false }; - -// only the files that represent f2 -var CORE_FILES = [ - { src: 'sdk/src/F2.js', minify: true }, - { src: 'sdk/src/classes.js', minify: true }, - { src: 'sdk/src/constants.js', minify: true }, - { src: 'sdk/src/events.js', minify: true }, - { src: 'sdk/src/rpc.js', minify: true }, - { src: 'sdk/src/ui.js', minify: true }, - { src: 'sdk/src/container.js', minify: true } -]; -var ENCODING = 'utf-8'; -var EOL = '\n'; -// files to be packaged -var PACKAGE_FILES = [ - // requirejs not yet necessary - // { src: 'sdk/src/third-party/require.min.js', minify: false }, - { src: 'sdk/src/third-party/json2.js', minify: true }, - { src: 'sdk/src/third-party/jquery.min.js', minify: false }, - { src: 'sdk/src/third-party/bootstrap-modal.js', minify: true }, - { src: 'sdk/src/third-party/jquery.noconflict.js', minify: false }, - { src: 'sdk/src/third-party/eventemitter2.js', minify: true }, - { src: 'sdk/src/third-party/easyXDM/easyXDM.min.js', minify: false } -]; -var VERSION_REGEX = /^(\d+)\.(\d+)\.(\d+)$/; -var globalUpdateBranch = true; - -// a list of buildSteps that maps an argument to a function. the buildSteps need -// to be in order of dependency in case -a is passed -var buildSteps = [ - { arg: 'j', f: js }, - //{ arg: 'n', f: nuget }, Don't want to force users to have Nuget at this time - { arg: 'l', f: less }, - { arg: 'd', f: docs }, - { arg: 'y', f: yuidoc }, - { arg: 'g', f: ghp } -]; - -// build all if no args are passed -argv.a = argv.a || process.argv.length == 2; - -// these have to be optionally inserted since we don't want them executing if -// -a is passed -if (argv['release-docs']) { - buildSteps.unshift({ arg: 'release-docs', f: releaseDocs }); -} -if (argv['release-sdk']) { - buildSteps.unshift({ arg: 'release-sdk', f: releaseSdk }); -} - -if (argv['prep']){ - argv.a = true; - buildSteps.unshift({ arg: 'prepareBranch', f: prepBranch }); -} - -// process -l if -d or -y is passed -argv.l = argv.l || argv.d || argv.y; - -// show help -if (argv.h) { - help(); -// release -} else if (argv.release) { - release(); -// Nuget -} else if (argv.n) { - nuget(); -// version -} else if (argv.v) { - version(); -// everything else -} else { - nextStep(); -} - -/** - * Process the list of buildSteps that were requested. We have to use a queue - * because of sync issues when running external processes. - * @method nextStep - */ -function nextStep() { - var option = buildSteps.shift(); - - if (!option) { return; } - - if (argv[option.arg] || argv.a) { - option.f(); - } else { - nextStep(); - } -} - -/** - * Ends execution and displays an error message - * @method die - */ -function die(message) { - console.error('ERROR: ' + message); - process.exit(1); -} - -/** - * Handlebars helper for precompilation - */ -handlebars.registerHelper('if', function(conditional, options) { - if (options.hash.desired === options.hash.test) { - return options.fn(this); - } -}); - -/** - * Build the documentation for GitHub Pages. The documentation generation is - * asynchronous, so if anything needs to execute after the generation is - * complete, those functions can be passed as parameters. - * @method docs - */ -function docs() { - console.log('Generating Docs...'); - - // files to run handlebar substitutions - var templateFiles = [ - './docs/src/template/baseTemplate.html', - './docs/src/template/style.html', - './docs/src/template/header.html', - './docs/src/template/footer.html', - './docs/src/index.md', - './docs/src/app-development.md', - './docs/src/container-development.md', - './docs/src/f2js-sdk.md', - './docs/src/extending-f2.md' - ], - dat = new Date(); - - // update Last Update Date and save F2.json - console.log("Setting last updated date..."); - f2Info.docs.lastUpdateDate = dat.toJSON(); - f2Info.docs.lastUpdateDateFormatted = dateFormat(dat); - saveF2Info(); - - processTemplateFile(templateFiles, f2Info, true); - console.log("Markitdown..."); - exec( - 'markitdown ./ --output-path ../ --docTemplate ./template/baseTemplate.html --header ./template/header.html --footer ./template/footer.html --head ./template/style.html --title "F2"', - { cwd:'./docs/src' }, - function(error, stdout, stderr) { - if (error) { - die(stderr); - } else { - processTemplateFileCleanup(templateFiles); - console.log('COMPLETE'); - - nextStep(); - } - } - ); -}; - -/** - * Date format helper for F2.json - * Returns date "October 15, 2012" - * - * @method dateFormat - * @param new Date() (optional) - */ -function dateFormat(dat){ - var dat = dat || new Date(), - month = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; - return dat.getDate() + " " + month[dat.getMonth()] + " " + dat.getFullYear(); -} - -/** - * Copies all documentation to the gh-pages folder - * @method ghp - */ -function ghp() { - console.log('Copying documentation to gh-pages...'); - wrench.copyDirSyncRecursive('./docs', '../gh-pages', { preserve:true }); - //delete the /src on gh-pages, we don't need it. - wrench.rmdirSyncRecursive('../gh-pages/src'); - console.log('COMPLETE'); - - nextStep(); -}; - -/** - * Display the help information - * @method help - */ -function help() { - optimist.wrap(80).showHelp(); -}; - -/** - * Build the debug, minified, and no-third-party sdk files - * @method js - */ -function js() { - var files, contents; - console.log('Building f2.no-third-party.js...'); - - files = [JS_HEADER] - .concat(CORE_FILES) - .concat([JS_FOOTER]); - - contents = files.map(function(f) { - return fs.readFileSync(f.src, ENCODING); - }); - - contents = processTemplate(contents.join(EOL), f2Info); - fs.writeFileSync('./sdk/f2.no-third-party.js', contents, ENCODING); - console.log('COMPLETE'); - - - console.log('Building Debug Package...'); - files = [JS_HEADER] - .concat(PACKAGE_FILES) - .concat(CORE_FILES) - .concat([JS_FOOTER]); - - contents = files.map(function(f) { - return fs.readFileSync(f.src, ENCODING); - }); - contents = processTemplate(contents.join(EOL), f2Info); - fs.writeFileSync('./sdk/f2.debug.js', contents, ENCODING); - console.log('COMPLETE'); - - - console.log('Building Minified Package...'); - contents = files.map(function(f) { - - var code = fs.readFileSync(f.src, ENCODING); - - if (f.minify) { - var comments = []; - var token = '"F2: preserved commment block"'; - - // borrowed from ender-js - code = code.replace(/\/\*![\s\S]*?\*\//g, function(comment) { - comments.push(comment); - return token; - //return ';' + token + ';'; - }); - - var ast = jsp.parse(code); // parse code and get the initial AST - ast = pro.ast_mangle(ast); // get a new AST with mangled names - ast = pro.ast_squeeze(ast); // get an AST with compression optimizations - code = pro.gen_code(ast); // compressed code here - - code = code.replace(RegExp(token, 'g'), function() { - return EOL + comments.shift() + EOL; - }); - } - - return code; - }); - contents = processTemplate(contents.join(';' + EOL), f2Info); - fs.writeFileSync('./sdk/f2.min.js', contents, ENCODING); - - // update Last Update Date and save F2.json - f2Info.sdk.lastUpdateDate = (new Date()).toJSON(); - saveF2Info(); - - console.log('COMPLETE'); - - //copy F2.min.js over to docs/js folder so it makes to gh-pages - console.log('Copying f2.min.js to ./docs/js/f2.js...'); - fs.copy('./sdk/f2.min.js', './docs/js/f2.js', function(err){ - if (err) { - die(err); - } else { - console.log("COMPLETE"); - // Issue #35 - console.log('Copying f2.min.js to ./docs/js/f2.min.js...'); - fs.copy('./sdk/f2.min.js', './docs/js/f2.min.js', function(err){ - if (err) { - die(err); - } else { - console.log("COMPLETE"); - - console.log('Copying F2.min.js to /f2.js...'); - fs.copy('./sdk/f2.min.js', './f2.js', function(err){ - if (err) { - die(err); - } else { - console.log("COMPLETE"); - nextStep(); - } - }); - } - }); - } - }); -}; - -/** - * Compile LESS into F2.css and F2.Docs.css - * @method less - */ -function less() { - console.log('Compiling LESS...'); - exec( - 'lessc ./template/less/bootstrap.less > ../css/F2.css --compress | lessc ./template/less/bootstrap-docs.less > ../css/F2.Docs.css --compress | lessc ./template/less/bootstrap-sdk.less > ../css/F2.Sdk.css --compress', - { cwd: './docs/src' }, - function(error, stdout, stderr){ - if (error){ - die(error); - } else { - console.log("COMPLETE"); - nextStep(); - } - } - ); -}; - -/** - * Build the Nuget package for publishing on Nuget.org - * @method nuget - */ -function nuget() { - console.log('Building Nuget Package...'); - processTemplateFile('./sdk/f2.nuspec', f2Info, true); - exec( - 'nuget pack f2.nuspec', - { cwd: './sdk' }, - function(error, stdout, stderr){ - if (error){ - die(error); - } else { - processTemplateFileCleanup('./sdk/f2.nuspec'); - console.log("COMPLETE"); - //nextStep(); - } - } - ); -}; - -/** - * Will replace handlebar placeholders for data - * @method processTemplate - * @param {string} content The template content or a path to the template content - * @param {object} data The data to replace - */ -function processTemplate(content, data) { - - // 'crossLink' is a special helper that is used by YUIdoc. We need to leave it - // alone so that it is processed later - handlebars.registerHelper('crossLink', function(path) { - return '{{#crossLink "' + path + '"}}{{/crossLink}}'; - }); - - // compile and run the template - var template = handlebars.compile(content); - return template(data); -}; - -/** - * Will replace handlebar placeholders for data within a file - * @method processTemplateFile - * @param {string|Array} filePath The path to the template content - * @param {object} data The data to replace - * @param {bool} [preserveOriginalFile] If true, the original file will be - * copied with a .temp extension. See - * {{#crossLink "processTemplateFileCleanup"}}{{/crossLink}} for how to cleanup - * the temp files that were created during this process - */ -function processTemplateFile(filePath, data, preserveOriginalFile) { - - filePath = [].concat(filePath); - - for (var i = 0; i < filePath.length; i++) { - if (fs.existsSync(filePath[i])) { - var content = fs.readFileSync(filePath[i], ENCODING); - if (preserveOriginalFile) { - fs.writeFileSync(filePath[i] + '.temp', content, ENCODING); - } - content = processTemplate(content, data); - fs.writeFileSync(filePath[i], content, ENCODING); - } - } -}; - -/** - * Copies the temp files created by processTemplateFile back to their original - * file names and removes the temp file - * @method processTemplateFileCleanup - * @param {string|Array} filePath The path to the template content - */ -function processTemplateFileCleanup(filePath) { - - filePath = [].concat(filePath); - - for (var i = 0; i < filePath.length; i++) { - if (fs.existsSync(filePath[i]) && fs.existsSync(filePath[i] + '.temp')) { - var content = fs.readFileSync(filePath[i] + '.temp', ENCODING); - fs.writeFileSync(filePath[i], content, ENCODING); - fs.unlinkSync(filePath[i] + '.temp'); - } - } -}; - -/** - * Creates a tag of "master" on GitHub using the SDK release version as the tag - * name - * @method release - */ -function release() { - - var v = argv.release; - - if (typeof v !== 'boolean') { - // git status - exec('git status', function(error, stdout, stderr) { - if (error) { - die(stderr); - } else if (/Changes not staged for commit/i.test(stdout) || /Changes to be committed/i.test(stdout)) { - die('Changes must be committed or stashed before creating a release'); - } else { - // no uncommitted changes, ready to proceed with the build queue - // we'll add a final step to the queue that will commit and tag the - // release in github. The first few steps will rebuild the files with - // the new version number - buildSteps.push({ - arg: 'release', - f: function() { - exec('git commit -a -m "Tagging ' + f2Info.sdk.version + ' release"', function(error, stdout, stderr) { - if (error) { - die(stderr); - } else { - exec('git tag ' + f2Info.sdk.version, function(error, stdout, stderr) { - if (error) { - die(stderr); - } else { - console.log('Release ' + f2Info.sdk.version + ' has been created\n\nYou must run the `git push origin ' + f2Info.sdk.version + '` command for the tag to make it to GitHub\n'); - } - }); - } - }); - } - }); - - // make sure these steps are run - argv.j = argv.y = argv.l = true; - - // run the queue by starting with releaseSdk - releaseSdk(argv.release); - } - }); - } else { - die('Version number must be passed with "--release" option'); - } -}; - -/** - * Updates the docs version and release date - * @method releaseDocs - * @param {string} [v] The version number - */ -function releaseDocs(v) { - - v = v || argv['release-docs']; - - if (typeof v !== 'boolean' && versionCheck(v)) { - f2Info.docs.releaseDate = (new Date()).toJSON(); - f2Info.docs.shortVersion = v.split('.').slice(0, 2).join('.'); - f2Info.docs.version = v; - saveF2Info(); - - console.log('Docs Version Updated: ', v); - nextStep(); - } else { - die('Version number must be passed with "--release-docs" option'); - } -}; - -/** - * Updates the SDK version and release date - * @method releaseSdk - * @param {string} [v] The version number - */ -function releaseSdk(v) { - - v = v || argv['release-sdk']; - - if (typeof v !== 'boolean' && versionCheck(v)) { - var parts = v.split('.'); - f2Info.sdk.releaseDate = (new Date()).toJSON(); - f2Info.sdk.shortVersion = v.split('.').slice(0, 2).join('.'); - f2Info.sdk.version = v; - saveF2Info(); - - console.log('SDK Version Updated: ', v); - nextStep(); - } else { - die('Version number must be passed with "--release-sdk" option'); - } -}; - -/** - * Gets and sets current git branch to F2.json - * @method setCurrentBranch - */ -function setCurrentBranch(callback){ - exec( - 'git rev-parse --abbrev-ref HEAD', - function(error,stdout,stderr){ - if (error){ - die(stderr); - } else { - //add git branch to F2Info - f2Info.branch = String(stdout).replace(/^\s*|\s*$/g, '');//replace empty strings - if (typeof callback == 'function'){ - callback(); - } - } - } - ); -} - -/** - * Saves the F2.json object back to a file - * Adds current git branch to file - * @method saveF2Info - */ -function saveF2Info() { - //sometimes we don't want to update the F2Info.branch property - if (!globalUpdateBranch){ - fs.writeFileSync('./build/F2.json', JSON.stringify(f2Info, null, '\t'), ENCODING); - return; - } - setCurrentBranch(function(){ - fs.writeFileSync('./build/F2.json', JSON.stringify(f2Info, null, '\t'), ENCODING); - }); -}; - -/** - * Display version information for F2 - * @method version - */ -function version() { - console.log([ - 'Docs Version: ' + f2Info.docs.version + ' (' + (new Date(f2Info.docs.releaseDate)).toDateString() + ')', - 'SDK Version: ' + f2Info.sdk.version + ' (' + (new Date(f2Info.sdk.releaseDate)).toDateString() + ')', - '' - ].join('\n')); -}; - -function versionCheck(version) { - if (!VERSION_REGEX.test(version)) { - die('Version number (' + version + ') must follow semver.org standards.\n\n'); - } - return true; -} - -/** - * Build the YUIDoc for the sdk - * @method yuidoc - */ -function yuidoc() { - - console.log('Generating YUIDoc...'); - - var builder, - docOptions = { - quiet: true, - norecurse: true, - paths: ['./sdk/src'], - outdir: './docs/sdk/', - themedir: './docs/src/sdk-template' - }, - json, - readmeMd = fs.readFileSync('README.md', ENCODING); - - json = (new Y.YUIDoc(docOptions)).run(); - // massage in some meta information from F2.json - json.project = { - docsAssets: '../', - version: f2Info.sdk.version, - docsVersion: f2Info.docs.version, - docsLastUpdateDateFormatted: f2Info.docs.lastUpdateDateFormatted, - branch: f2Info.branch - }; - docOptions = Y.Project.mix(json, docOptions); - - // hasClassMembers - // ensures that the class has members and isn't just an empty namespace - Y.Handlebars.registerHelper('hasClassMembers', function() { - - for (var i = 0, len = json.classitems.length; i < len; i++) { - //console.log(json.classitems[i].class, this.name); - if (json.classitems[i].class === this.name) { - return ''; - } - } - - return 'hide'; - }); - - // title tag - Y.Handlebars.registerHelper('htmlTitle',function () { - var name = this.displayName || this.name, - title = name; - - if (title) { - title = 'F2 - ' + title; - } else { - title = 'F2 - The Open Financial Framework'; - } - - return title; - }); - - // insert readme markdown - Y.Handlebars.registerHelper('readme', function() { - return builder.markdown(readmeMd, true); - }); - - builder = new Y.DocBuilder(docOptions, json); - builder.compile(function() { - console.log('COMPLETE'); - nextStep(); - }); -}; - -/** - * Added new build step to prepare branches refd in pull requests so they - * can more easily be merged from Github without re-building templates in 'master'. - * - * This step is just for @markhealey and @brianbaker - */ -function prepBranch(){ - console.log("Preparing current branch for merging into master..."); - //set this to false to tell docs() not to touch branch prop - //before compiling templates - globalUpdateBranch = false; - //add FIRST step to update F2.json - buildSteps.unshift({ - arg: 'updateF2Info', - f: function() { - //sync-up dates - var dat = new Date(); - f2Info.docs.releaseDate = f2Info.sdk.releaseDate = dat.toJSON(); - f2Info.docs.lastUpdateDate = f2Info.sdk.lastUpdateDate = dat.toJSON(); - f2Info.docs.lastUpdateDateFormatted = dateFormat(dat); - //set branch name - f2Info.branch = 'master'; - //save - saveF2Info(); - //go - nextStep(); - } - }); - //add step to display version # (serenity now)...and then we're done. - buildSteps.push({ - arg: 'versCheck', - f: function(){ - version(); - console.log("PREP COMPLETE -- You can now commit changes & merge with 'master'."); - } - }); - //run all build steps - //start queue - nextStep(); -} diff --git a/docs/app-development.html b/docs/app-development.html index 21d6a911..7d14ebfb 100644 --- a/docs/app-development.html +++ b/docs/app-development.html @@ -3,10 +3,9 @@ F2 - App Development - - + - + @@ -17,7 +16,7 @@ - + @@ -26,7 +25,7 @@ - + @@ -119,13 +118,13 @@ -
-
+
+

App Development

-You've come to the right place if you want to start building F2 apps. Before continuing, make sure you've cloned the F2 repository on GitHub or downloaded the latest framework build (v1.1.2). Secondly, read about the F2 Framework. There are a few important concepts to help you better understand apps, containers and context. +Apps are the building blocks of any F2-enabled solution. F2 apps are web-based, are built with HTML5, CSS3, and JavaScript, and contain entitled data. F2 enables App Developers to build once and deploy to any container with a simple and modern API. You can read more about the framework, download the project on GitHub or get started below. The latest version of F2 is 1.2.0.

F2 apps are synonymous with modules, widgets and portlets. Think charts, portfolios, trade tickets, and screeners. F2 apps only need to be programmed once, no matter where they will be used. To start, F2 Apps are either:

@@ -149,90 +148,59 @@

App Development


Get Started

-

To help you get started building an F2 app, browse through the resources below. To jump start your F2 app development, download the F2 app template (which now includes a basic container) or follow the instructions below.

+

To help you get started building an F2 app, review the documentation and examples below. To jump start your F2 container or app development, download the F2 example container and apps.

-Download Basic F2 App Template +Download F2 Examples

Basic Container

-

To begin, you do not need to build F2 as described in the readme on GitHub. Simply download Bootstrap and save a local copy of F2.js. Also ensure you're properly configured.

+

To begin, you do not need to build (or compile) F2 as described in the readme on GitHub. Simply download F2.js and Bootstrap, and ensure you're properly configured for continuing with the documentation.

-Download F2.js Download Bootstrap +Download F2.js Download Bootstrap

-

Create your basic container HTML template:

+

Setup a basic container HTML template (or add F2.js to an existing website):

<!DOCTYPE html>
-<html>
     <head>
         <title>F2 Container</title>
         <link rel="stylesheet" href="/path/to/your/bootstrap.css">
     </head>
     <body>
-        <div class="container">
-            <div class="hero-unit">
-                <h1>Hello F2</h1>
-            </div>
-            <div class="row"><!--apps go here--></div>
-        </div>
-        <!--include jQuery & Bootstrap-->
-        <script src="http://code.jquery.com/jquery-latest.js"></script>
-        <script src="/path/to/your/bootstrap.js"></script>
-        <!--include F2.js-->
         <script src="/path/to/your/F2.js"></script>
-        <!--init & register-->
-        <script>
-            (function(){
-                //define AppConfigs
-                var _appConfigs = [{
-                    appId: "com_your_app_id",
-                    description: "F2 app description",
-                    name: "F2 App",
-                    manifestUrl: "/path/to/your/manifest.js" //note the path to your manifest! 
-                }];
-                //Setup ContainerConfig
-                F2.init({
-                    beforeAppRender: function(app){
-                        var appRoot = '<section class="well span12"></section>';
-                        return $(appRoot).appendTo('div.row');
-                    },
-                    afterAppRender: function (app, html) {
-                        //app.root is `appRoot` from beforeAppRender()
-                        return $(app.root).append(html);
-                    }
-                }); 
-                F2.registerApps(_appConfigs); //pass _appConfigs to initialize apps
-            })();
-        </script>
     </body>
 </html>
-

In developing a more advanced container, the HTML document's body element would contain additional markup and allow for specific positioning or placement of apps. Additionally, more advanced containers could introduce features and functionality to their apps in the form of authentication APIs, streaming data feeds, federated search, etc. All containers must follow the F2 design guidelines.

-
-
-

Basic App

-

Create your basic F2 app manifest and save it as /path/to/your/manifest.js using this code below. Note the path to this file should be specified in the manifestUrl property within the _appConfigs array in your basic container (shown above).

-
F2_jsonpCallback_com_your_app_id({
-    "scripts": [],   
-    "styles": [],   
-    "apps": [{
-        "data": {},
-        "html": "<div><p>Hello, world. I'm an F2 app.</p></div>"
-    }]
-})
-

Note You can download the F2 app template instead of creating the basic app by hand.

+

Note In developing a more advanced container, the HTML document's body element would contain additional markup and allow for specific positioning or placement of apps. Additionally, more advanced containers could introduce features and functionality to their apps in the form of authentication APIs, streaming data feeds, federated search, etc. All containers must follow the F2 design guidelines.

+
+
+

Basic AppConfig

+
var _appConfig = {
+    appId: 'com_openf2_examples_javascript_helloworld',
+    manifestUrl: 'http://docs.openf2.org/demos/apps/JavaScript/HelloWorld/manifest.js'
+};
+ 
+$(function(){
+    F2.init();
+    F2.registerApps(_appConfig);
+});
+

Note For more information about the AppConfig, read up on them in Container Development: App Integration.

Testing the Basics

-

Now with a basic container and a basic app, you can load your F2 container and expect to see:

-
-
-
+

Now with a basic container and a basic app, combine these two for a working example. Press Result in this jsfiddle.

+ +

In getting to this point, you've only scratched the surface of F2 containers and apps. Continue reading and understanding the F2 spec to build exactly the financial solutions that our customers want.

Sample Apps and Container

-

Good news! In the project repo on GitHub, you will find a basic container along with a number of sample apps which demonstrate functionality far beyond the basic app above. Once you clone or download the project repository, open the sample container by pointing your browser at:

-

http://localhost/F2/examples/container/

+

Good news! In the project repo on GitHub, you will find a basic container along with a number of sample apps which demonstrate functionality far beyond the basic app above. Once you clone or download the project repository, open the sample container by pointing your browser at:

+

http://localhost/F2/examples/

+

These examples are also available in a separate archive if you don't want to download the entire repository.

+

+Download F2 Examples +

+

Configuration

@@ -241,7 +209,7 @@

Configuration

To get started working with or developing containers, browse to the documentation for developing the container.

Ready to start coding?

-Developing F2 Apps F2.js SDK Reference +Developing F2 Apps F2.js SDK Reference


@@ -284,7 +252,7 @@

Setting Up Your Project

-

Helper Download the F2 app starter zip or read about setting up a basic container and app in Getting Started.

+

Helper Download the F2 examples or read about setting up a basic container and app in Getting Started.

App Manifest

@@ -624,7 +592,7 @@

Container-to-App Context

F2.Events.on(
     F2.Constants.Events.CONTAINER_SYMBOL_CHANGE, 
     function(data){
-        F2.log("The symbol was changed to " + data.symbol);
+        F2.log("The symbol was changed to " + data.symbol);
     }
 );

The F2.Events.on() method accepts the event name and listener function as arguments. Read the SDK for more information.

@@ -667,7 +635,7 @@

App-to-Container Context

F2.Events.on(
     F2.Constants.Events.APP_SYMBOL_CHANGE, 
     function(data){
-        F2.log("The symbol was changed to " + data.symbol);
+        F2.log("The symbol was changed to " + data.symbol);
     }
 );

Note For a full list of support event types, browse to the SDK for F2.Constants.Events.

@@ -690,8 +658,8 @@

App-to-App Context

F2.Events.on(
     "buy_stock", 
     function(data){
-        if (data.isAvailableToPurchase){
-            F2.log("Trade ticket order for " + data.symbol + " at $" + data.price);
+        if (data.isAvailableToPurchase){
+            F2.log("Trade ticket order for " + data.symbol + " at $" + data.price);
         } else {
             F2.log("This stock is not available for purchase.")
         }
@@ -743,7 +711,7 @@ 

More Complex Context

F2.Events.on(
     "buy_stock", 
     function(data){
-        F2.log("Trade ticket order for " + data.symbol + " at $" + data.price);
+        F2.log("Trade ticket order for " + data.symbol + " at $" + data.price);
         //..populate the trade ticket...
         //fire the callback
         if (typeof data.callback === 'function'){
@@ -793,8 +761,7 @@ 

Testing Your App

If you open ~/F2/examples/container/js/sampleApps.js in your text editor, you'll find a list of sample F2 apps broken down by programming language. Simply modify this file to your liking and add your app anywhere in the appropriate array (JavaScript, PHP or C#). The configuration is comprised of F2.AppConfig properties, and the following are the minimum required properties.

{
     appId: "com_companyName_appName",
-    manifestUrl: "http://www.domain.com/manifest.js",
-    name: "App name"
+    manifestUrl: "http://www.domain.com/manifest.js"
 }

For full details on these F2.AppConfig properties and all the others, browse the F2.js SDK documentation.


@@ -842,7 +809,7 @@

Showing or Hiding Loading Spinners

}).done(function(jqxhr){ F2.log(jqxhr); //hide loading - this.ui.hideMask(this.root); + this.ui.hideMask(this.root); }); ...

The showMask() method takes two arguments: a DOM element where to show the mask and a boolean indicating whether or not to show a spinning graphic.

@@ -918,7 +885,7 @@

Setting Up Views

Controlling View State

The F2.UI namespace provides an API for developers to manage F2 app View state.

To programmatically change a View in javascript:

-
appConfig.ui.Views.change(F2.Constants.Views.HOME);
+
appConfig.ui.Views.change(F2.Constants.Views.HOME);

Note When appConfig.ui.Views.change() is called, the hide classname is automatically added or removed by F2.js depending on the visibility of the view. Read more in the SDK docs.

In F2 app HTML, you can use a combination of CSS classnames and data- attributes to provide UI elements making it easy for users to navigate between Views.

For example, an app has two views: "home" and "about". On the "home" View, a button allows the user to navigate to the "about" view. In the presence of the classname f2-app-view-trigger and the data-f2-view data attribute, F2.js automatically adds a javascript event to the button.

@@ -975,8 +942,8 @@

Considerations


@@ -984,8 +951,7 @@

Considerations

- - - + + \ No newline at end of file diff --git a/docs/container-development.html b/docs/container-development.html index 68455cad..54e2b590 100644 --- a/docs/container-development.html +++ b/docs/container-development.html @@ -3,10 +3,9 @@ F2 - Container Development - - + - + @@ -17,7 +16,7 @@ - + @@ -26,7 +25,7 @@ - +
@@ -94,19 +93,19 @@
- + @@ -119,102 +118,71 @@
-
-
+
+

Container Development

-You've come to the right place if you want to start building F2 containers. Before continuing, make sure you've cloned the F2 repository on GitHub or downloaded the latest framework build (v1.1.2). Secondly, read about the F2 Framework. There are a few important concepts to help you better understand apps, containers and context. +The container is the foundation of any F2-enabled solution. By leveraging the F2.js SDK, Container Providers offer a consistent and reliable mechanism for all App Developers to load their apps on that container regardless of where it is hosted, who developed it, or what back-end stack it uses. You can read more about the framework, download the project on GitHub or get started below. The latest version of F2 is 1.2.0.


Get Started

-

To help you get started building an F2 container, browse through the resources below. To jump start your F2 container and app development, download the F2 template (which now includes a basic container) or follow the instructions below.

+

To help you get started building an F2 container, review the documentation and examples below. To jump start your F2 container or app development, download the F2 example container and apps.

-Download Basic F2 Container Template +Download F2 Examples

Basic Container

-

To begin, you do not need to build F2 as described in the readme on GitHub. Simply download Bootstrap and save a local copy of F2.js. Also ensure you're properly configured.

+

To begin, you do not need to build (or compile) F2 as described in the readme on GitHub. Simply download F2.js and Bootstrap, and ensure you're properly configured for continuing with the documentation.

-Download F2.js Download Bootstrap +Download F2.js Download Bootstrap

-

Create your basic container HTML template:

+

Setup a basic container HTML template (or add F2.js to an existing website):

<!DOCTYPE html>
-<html>
     <head>
         <title>F2 Container</title>
         <link rel="stylesheet" href="/path/to/your/bootstrap.css">
     </head>
     <body>
-        <div class="container">
-            <div class="hero-unit">
-                <h1>Hello F2</h1>
-            </div>
-            <div class="row"><!--apps go here--></div>
-        </div>
-        <!--include jQuery & Bootstrap-->
-        <script src="http://code.jquery.com/jquery-latest.js"></script>
-        <script src="/path/to/your/bootstrap.js"></script>
-        <!--include F2.js-->
         <script src="/path/to/your/F2.js"></script>
-        <!--init & register-->
-        <script>
-            (function(){
-                //define AppConfigs
-                var _appConfigs = [{
-                    appId: "com_your_app_id",
-                    description: "F2 app description",
-                    name: "F2 App",
-                    manifestUrl: "/path/to/your/manifest.js" //note the path to your manifest! 
-                }];
-                //Setup ContainerConfig
-                F2.init({
-                    beforeAppRender: function(app){
-                        var appRoot = '<section class="well span12"></section>';
-                        return $(appRoot).appendTo('div.row');
-                    },
-                    afterAppRender: function (app, html) {
-                        //app.root is `appRoot` from beforeAppRender()
-                        return $(app.root).append(html);
-                    }
-                }); 
-                F2.registerApps(_appConfigs); //pass _appConfigs to initialize apps
-            })();
-        </script>
     </body>
 </html>
-

In developing a more advanced container, the HTML document's body element would contain additional markup and allow for specific positioning or placement of apps. Additionally, more advanced containers could introduce features and functionality to their apps in the form of authentication APIs, streaming data feeds, federated search, etc. All containers must follow the F2 design guidelines.

-
-
-

Basic App

-

Create your basic F2 app manifest and save it as /path/to/your/manifest.js using this code below. Note the path to this file should be specified in the manifestUrl property within the _appConfigs array in your basic container (shown above).

-
F2_jsonpCallback_com_your_app_id({
-    "scripts": [],   
-    "styles": [],   
-    "apps": [{
-        "data": {},
-        "html": "<div><p>Hello, world. I'm an F2 app.</p></div>"
-    }]
-})
-

Note You can download the F2 container/app template instead of creating the basic app by hand.

+

Note In developing a more advanced container, the HTML document's body element would contain additional markup and allow for specific positioning or placement of apps. Additionally, more advanced containers could introduce features and functionality to their apps in the form of authentication APIs, streaming data feeds, federated search, etc. All containers must follow the F2 design guidelines.

+
+
+

Basic AppConfig

+
var _appConfig = {
+    appId: 'com_openf2_examples_javascript_helloworld',
+    manifestUrl: 'http://docs.openf2.org/demos/apps/JavaScript/HelloWorld/manifest.js'
+};
+ 
+$(function(){
+    F2.init();
+    F2.registerApps(_appConfig);
+});
+

Note For more information about the AppConfig, read up on them in App Integration.

Testing the Basics

-

Now with a basic container and a basic app, you can load your F2 container and expect to see:

-
-
-
+

Now with a basic container and a basic app, combine these two for a working example. Press Result in this jsfiddle.

+ +

In getting to this point, you've only scratched the surface of F2 containers and apps. Continue reading and understanding the F2 spec to build exactly the financial solutions that our customers want.

Sample Apps and Container

-

Good news! In the project repo on GitHub, you will find a basic container along with a number of sample apps which demonstrate functionality far beyond the basic app above. Once you clone or download the project repository, open the sample container by pointing your browser at:

-

http://localhost/F2/examples/container/

+

Good news! In the project repo on GitHub, you will find a basic container along with a number of sample apps which demonstrate functionality far beyond the basic app above. Once you clone or download the project repository, open the sample container by pointing your browser at:

+

http://localhost/F2/examples/

+

These examples are also available in a separate archive if you don't want to download the entire repository.

+

+Download F2 Examples +

+

Configuration

@@ -223,7 +191,7 @@

Configuration

To get started working with or developing apps, browse to the documentation for developing apps.

Ready to start coding?

-Developing F2 Containers F2.js SDK Reference +Developing F2 Containers F2.js SDK Reference


@@ -235,21 +203,20 @@

Container Design

If App Developers embed URLs back to their own websites or to third party sites, URLs must be opened in a new window as to not interrupt the experience of someone using the container. If authentication is required on an App Developer's site, this can be accomplished with pass-through authentication using encrypted URLs as discussed in Single Sign On.

Choices

-

In order to ensure that apps built using F2 are successful, they must be accessible. As such, F2 made choices for which open-source libraries and frameworks would be leveraged to reduce the level of effort across F2 adopters.

+

In order to ensure that containers built using F2 are successful, they must be accessible. As such, F2 made choices for which open-source libraries and frameworks would be leveraged to reduce the level of effort across F2 adopters.

Read more about those choices in the Framework.

-

Ultimately, the responsibility of app design falls on either the Container or App Developer. In many cases, Container Developers will provide App Developers will visual designs, style guides or other assets required to ensure apps have the form and function for a given container. Container Developers may also provide CSS for App Developers to adhere to—which should be easy since F2 enforces a consistent HTML structure across all containers and apps.

+

Ultimately, the responsibility of app design falls on either the Container or App Developer or both. In many cases, Container Developers will provide App Developers will visual designs, style guides or other assets required to ensure apps have the form and function for a given container. Container Developers may also provide CSS for App Developers to adhere to—which should be easy since F2 enforces a consistent HTML structure across all containers and apps. In other cases, Container and App Developers may never know each other and it's important everyone strictly adheres to the guidelines set forth in this documentation.


Developing F2 Containers

-

A container is a browser-based desktop-like application which brings F2 apps together onto a seamless user interface. It also can provide horsepower to its apps in the form of request-response web services or streaming data feeds.

+

A container is a browser-based web application which brings F2 apps together onto a seamless user interface. It can also provide data and user context to its apps in the form of request-response web services or streaming data feeds.

Including the F2 SDK

For a webpage to be considered an F2 container, it must first include the F2.js JavaScript SDK. This is as simple as downloading the F2 project from GitHub and adding a script tag to the page.

-
<script src="/path/to/your/container/f2.js"></script>
-

You will find a basic container in the project repo on GitHub along with a number of sample apps.

-

Once the script tag has been added, it is up to the Container Developer to configure and customize the container. The first step is getting a ContainerID.

+
<script src="/path/to/your/f2.js"></script>
+

You will find a basic container in the project repo on GitHub along with a number of sample apps. Once the script tag has been added, it is up to the Container Developer to configure and customize the container. The first step is getting a ContainerID.

F2 ContainerID

@@ -268,95 +235,406 @@

F2 ContainerID

Setting Up Your Project

Once you have your ContainerID, start by setting up your container project. You will need at least one configuration in addition to an HTML page: the app configs. (In the GitHub repository, an example is found in /examples/container/js/sampleApps.js.) This doesn't need to be a static javascript file like sampleApps.js but the structure and format of the app configs is important.

-
-

App Configs

-

An F2 Container Provider must deliver the app configs to its container before calling F2.init(). The app configurations are represented quite simply as a list of AppConfig objects. These could be stored in a JavaScript array or in an enterprise-class database. AppConfig objects contain app meta data provided by the App Developer when he creates his app in the Developer Center.

-

Example AppConfig object from an individual app:

+
+

Container Config

+

The F2.js JavaScript SDK provides an API for providers to configure their containers. Every container must be setup using ContainerConfig and the methods available, however if the F2 defaults are acceptable, the ContainerConfig is not required.

+

To initialize a container using F2 defaults, call this function:

+
F2.init();
+

To initialize a container with a ContainerConfig, use:

+
F2.init({
+    UI: {},
+    xhr: function(){},
+    supportedViews: []  
+});
+

Review all of the ContainerConfig properties in the reference documentation.

+

To see a more detailed example of F2.init(), look at the sample container javascript in the F2 repo on GitHub.

+
+

AppRender, BeforeAppRender, AfterAppRender

+

The appRender(), beforeAppRender(), and afterAppRender() methods were deprecated in F2 version 1.2 in favor of F2.AppHandlers. Upgrading to F2 1.2 will not break existing containers using any of these methods as they are still present in the SDK.

+

For more information, see AppHandlers for App Layout.

+
+
+

Setting Up a Loading GIF

+

Container Developers have the opportunity to customize some user interface (UI) elements which propagate to the App Developers' toolkit in F2.js. One of those is F2.UI.Mask. The Mask object contains configuration defaults for the F2.UI.showMask() and F2.UI.hideMask() methods.

+

An example of setting the mask in F2.init():

+
F2.init({
+    UI:{
+        Mask:{
+            loadingIcon:'./img/spinner.gif',
+            backgroundColor: '#fff',
+            opacity: 0.5
+        }
+    }
+});
+

Included in the F2.UI.Mask configuration object are the following properties: backgroundColor, loadingIcon, opacity, useClasses, and zIndex. Each of these F2.UI.Mask properties is detailed in the F2.js SDK docs.

+

For more information on F2.UI, browse to the F2.js SDK docs.

+
+
+

Override the AppManifest Request

+

Occasionally Container Developers need more granular control over the AppManifest request mechanism in F2.js. The manifest request process—intentionally obscured from developers through the F2.registerApps() API—is handled by a simple ajax call to an HTTP endpoint. (F2 relies on jQuery.ajax() for this.) In version 1.2.0 of F2, the AppManifest request can be overridden in the Container Config.

+

Note The AppManifest endpoint is configured in the manifestUrl property within each AppConfig.

+

The following example demonstrates how the xhr property of the ContainerConfig is used to override F2.js.

+
F2.init({
+    xhr: function(url, appConfigs, success, error, complete) {
+        $.ajax({
+            url: url,
+            type: 'POST',
+            data: {
+                params: F2.stringify(appConfigs, F2.appConfigReplacer)
+            },
+            jsonp: false, // do not put 'callback=' in the query string
+            jsonpCallback: F2.Constants.JSONP_CALLBACK + appConfigs[0].appId, // Unique function name
+            dataType: 'json',
+            success: function(appManifest) {
+                // custom success logic
+                success(appManifest); // fire success callback
+            },
+            error: function() {
+                // custom error logic
+                error(); // fire error callback
+            },
+            complete: function() {
+                // custom complete logic
+                complete(); // fire complete callback
+            }
+        });
+    }
+});
+
+
+

Extending XHR

+

The F2.ContainerConfig.xhr property has two additional customizable properties available: dataType and type.

+
+
DataType
+

The dataType property allows the container to override the request data type (JSON or JSONP) that is used for the request. Using JSON as a dataType is only available for F2 apps running on the same domain as the container.

+
F2.init({
+    xhr: {
+        dataType: function(url) {
+            return F2.isLocalRequest(url) ? 'json' : 'jsonp';
+        }
+    }
+});
+
+
+
Type
+

The type property allows the container to override the request method that is used (similar to the type parameter to jQuery.ajax()). Since HTTP POST is not supported on JSONP requests, using POST as a type is only available for F2 apps using JSON and are therefore running on the same base domain as the container.

+
F2.init({
+    xhr: {
+        type: function(url) {
+            return F2.isLocalRequest(url) ? 'POST' : 'GET';
+        }
+    }
+});
+

For more information on F2.ContainerConfig.xhr, browse to the F2.js SDK docs.

+
+
+
+

Supported Views

+

F2 Container Developers should define which app views their container supports. This is set in the supportedViews property of the ContainerConfig using F2.Constants.Views.

+
F2.init({
+    supportedViews: [F2.Constants.Views.HOME, F2.Constants.Views.SETTINGS, F2.Constants.Views.REMOVE]
+});
+

Note Every F2 app has a home view (whether defined by the App Developer or not). This means if no views are provided by the App Developer, a home view is automatically added to appConfig.views during the app registration process inside F2.

+
+
+

Secure Apps

+

For information about how to configure secure apps (i.e., load 3rd party apps in an isolated iframe) on a container, read about Secure Apps.

+
+
+

Container Templates

+

If you're looking for sample container HTML template code, jump to the Get Started section.

+
+
+
+
+
+

App Integration

+

There are two ways of integrating apps on a container: requesting apps on-demand (via HTTP) or by linking pre-loaded apps. Requesting apps on-demand when the container loads is the traditional way of integrating apps with F2. Incorporating apps which have been pre-fetched or are otherwise already on the container when it loads is an alternative method. The following sections describe both of these methods in detail.

+

The process of loading apps on a container occurs by using a method called F2.registerApps(). The Container Developer must call this method—which accepts two arguments: one required, one optional— after F2.init() is called. If this method isn't called, no apps can be loaded on the container.

+

The two arguments provided to registerApps() are an array of AppConfig objects and, optionally, an array of AppManifest objects. As F2.js parses each AppConfig, the apps are validated, hydrated with some additional properties, and saved in browser memory on the container. Regardless of where the container's AppConfig object is defined (hard-coded or via API), integrating apps is a simple process.

+
+

AppConfigs

+

Before continuing, let's discuss the AppConfig. The container-provided app configurations are represented simply as an array of AppConfig objects. These could be configured statically or fetched from an F2 Registry API. AppConfig objects contain app meta data—including the manifestUrl—provided by the App Developer when an app is registered in the Developer Center.

+

An example AppConfig object from an individual app:

{
     appId: "com_companyName_appName",
-    description: "App description",
-    height: 500,
     manifestUrl: "http://www.domain.com/manifest.js",
-    name: "App name"
+    name: "App name",
+    context: {
+        data: [1,2,3,4,5]
+    }
 }
-

Example array of AppConfig objects for a collection of apps:

-
var _appConfigs = [
+

An example array of AppConfig objects for a collection of apps:

+
[
     {
         appId: "com_companyName_appName",
-        description: "App description",
-        height:500,
         manifestUrl: "http://www.domain.com/manifest.js",
-        name: "App name"
+        name: "App name",
+        context: {
+            data: [1,2,3,4,5]
+        }
     },
     {
         appId: "com_companyName_appName2",
-        description: "App2 description",
-        height:100,
-        manifestUrl: "http://www.domain2.com/manifest.js",
-        name: "App2 name"
+        manifestUrl: "http://www.domain.com/manifest2.js",
+        name: "App2 name",
+        context: {
+            name: 'value'
+        }
     },
     {
         appId: "com_companyName_appName3",
-        description: "App3 description",
-        height:200,
-        manifestUrl: "http://www.domain3.com/manifest.js",
-        name: "App3 name"
-    }
+        manifestUrl: "http://www.domain.com/manifest3.js",
+        name: "App3 name",
+        context: {
+            status: 'ok'
+        }
+    },
 ];
-
-

Container Config

-

The F2.js JavaScript SDK provides an API for providers to configure their containers. Every container must be setup using ContainerConfig and the methods available.

-

In the container's $(document).ready(), add the F2.init():

-
$(document).ready(function(){
-    F2.init({
-        //define ContainerConfig properties
-        appRender: function(appConfig, html){ ... },
-        beforeAppRender: function(appConfig, html){ ... },
-        afterAppRender: function(appConfig){ ... }
-    });
+
+

Requesting Apps On-Demand

+

Requesting apps on-demand when the container loads is the traditional way of integrating apps with F2. For the purposes of this example, we will use an example news app from OpenF2.org.

+

Let's look at some container code.

+
+

Static App Configuration

+

First, we define the AppConfig in a hard-coded _appConfig variable. This example demonstrates only a single app; if there were multiple apps, _appConfig would be an array of objects versus an object literal. Secondly, when the document is ready, F2.init() is called and subsequently F2.registerApps() with the single argument.

+ + +

This javascript code will insert the example news app into the container's <body>. Press Result in the jsfiddle above to try this demo.

+

Note If more granular control is needed for app placement, use F2.AppHandlers functionality. Read about that in AppHandlers for App Layout.

+
+
+

Dynamic App Configuration

+

As an alternative to static app configuration shown above, the _appConfig variable could be assigned the result of an API call to the F2 Registry. The Registry API response is designed to match the structure of the AppConfig for passing the JSON straight through to F2 in your code. Whether your app configuration JSON comes from the F2 Registry or your own database is irrelevant; the process is identically the same as shown in this example.

+ + +

About this jsfiddle To simulate an ajax request, this example uses jsfiddle's echo feature. Simply replace the getAppConfigs function with your own ajax request and ignore the echoData variable.

+
+
+
+

Registering Pre-Loaded Apps

+

Incorporating apps which have been pre-loaded or are otherwise already on the container when it loads is an alternative method to integrating F2 apps. This method is useful when the container is being constructed on the server-side (at run-time or on a schedule) and F2 functionality is desired. To use pre-loaded apps, the Container Developer is required to make a request to each apps' AppManifest and its dependencies before the page is rendered.

+

For the following example, let's assume you have a web page composed on the server and all of its HTML is delivered to the browser in one payload. This page also has at least one widget (or component) you'd like to register with F2.js.

+
+

1. Setup Container

+

To use pre-loaded apps, a web page with a placeholder element for the apps is required. This simple (and empty) web page features a div#news_app.span12 which serves as that placeholder or "root" element.

+
<!DOCTYPE html>
+    <head>
+        <title>F2 Container</title>
+        <link rel="stylesheet" href="/path/to/your/bootstrap.css">
+    </head>
+    <body>
+        <div class="container">
+            <div class="row">
+                <div class="span12" id="news_app">
+                    <!--app goes here-->
+                </div>
+            </div>
+        </div>
+        <script src="/path/to/your/F2.js"></script>
+    </body>
+</html>
+
+
+

2. Request AppManifest

+

Next, make a server-side request to the news apps' AppManifest—the URL is found in manifestUrl—and capture the resulting JSON. Each AppManifest contains scripts, style sheets and HTML (more about the AppManifest). The market news apps' AppManifest looks like this:

+
{
+   "apps":[{
+         "data":{},
+         "html": "<div data-module-name=\"MarketNewsApp\">...</div>",
+    }],
+   "scripts":[
+      "http://www.openf2.org/js/main.js"
+   ],
+   "styles":[
+      "http://www.openf2.org/css/site.css"
+   ]
+}
+

Note Parts of this AppManifest were intentionally removed for legibility, including the required JSONP function name (F2_jsonpCallback_com_openf2_examples_csharp_marketnews). The full AppManifest is available on OpenF2.org.

+
+
+Performance Tip +
+ +Container Developers can use the AppConfig and pre-loaded AppManifest (from step 2 above) in conjunction with F2.registerApps() to speed up the loading of F2 containers. For more information, browse to Combining AppConfig and AppManifest. +
+ +
+
+

3. Add App to Container

+

You're almost there. Next, embed the news app's html, scripts and styles. The F2 app is inserted into .row > .span12 following Bootstrap's scaffolding guidelines. The styles were appended to the head and the scripts were appended to the body (in this case just one URL for each).

+
<!DOCTYPE html>
+    <head>
+        <title>F2 Container</title>
+        <link rel="stylesheet" href="/path/to/your/bootstrap.css">
+        <link rel="stylesheet" href="http://www.openf2.org/css/site.css">
+    </head>
+    <body>
+        <div class="container">
+            <div class="row">
+                <div class="span12" id="news_app">
+                    <div data-module-name="MarketNewsApp" id="news_app">...</div>
+                </div>
+            </div>
+        </div>
+        <script src="/path/to/your/F2.js"></script>
+        <script src="http://www.openf2.org/js/main.js"></script>
+    </body>
+</html>
+

The example news app is now part of the web page and everything should be functioning properly. The final step is to register the app with F2.

+
+
+

4. Assign Root Element to AppConfig

+

To use pre-loaded apps, an additional property is required on the AppConfig object. It is called root and can be either a CSS selector string or a DOM element. Regardless of type, F2 will parse the value of root and it must return an existing in-page DOM element. Furthermore, the value of root must represent a unique DOM element as each app needs its own containing, or root, element.

+
var _appConfig = {
+    appId: 'com_openf2_examples_csharp_marketnews',
+    description: 'Example News',
+    manifestUrl: 'http://www.openf2.org/Examples/Apps',
+    name: 'Example News',
+    root: document.getElementById('news_app')
+};
+

Both of these are valid values for the root property.

+

Using JavaScript:

+
{
+    root: document.getElementById('news_app')
+}
+

Using a CSS selector string:

+
{
+    root: '#news_app'
+}
+

F2.js uses jQuery internally to parse the value of the root property and, in turn, jQuery relies on the Sizzle javascript selector library. If a CSS selector string is assigned to root, it must be a valid CSS 3 selector supported by Sizzle. Refer to the Sizzle documentation for more details.

+
+
+

5. Register App

+

Since you started with the AppConfig and now have the AppManifest from step 2 along with an HTML page containing the embedded app, all that remains is a simple call to F2. Registering pre-loaded apps with F2.js means passing the ammended AppConfig as shown in the example below.

+
var _appConfig = {
+    appId: 'com_openf2_examples_csharp_marketnews',
+    description: 'Example News',
+    manifestUrl: 'http://www.openf2.org/Examples/Apps',
+    name: 'Example News',
+    root: document.getElementById('news_app')
+};
+
+$(function(){
+    F2.init();
+    F2.registerApps(_appConfig);
 });
-

To see an more detailed example of F2.init(), look at the sample container javascript file in the F2 repo on GitHub.

-
-

AppRender

-

The appRender() method allows the container to wrap an app in extra HTML. The function should accept an F2.AppConfig object and also a string of HTML. The extra HTML can provide links to edit app settings and remove an app from the container. See F2.Constants.Css for CSS classes that should be applied to elements.

-
-
-

BeforeAppRender

-

The beforeAppRender() method allows the container to render HTML for an app before the AppManifest for an app has loaded. This can be useful if the design calls for loading spinners to appear for each app before each app is loaded and rendered to the page.

-
-
-

AfterAppRender

-

The afterAppRender() method allows the container to override how an app's HTML is inserted into the page. The function should accept an F2.AppConfig object and also a string of HTML.

-

For more information on F2.ContainerConfig, browse to the F2.js SDK docs.

-
-
-

F2 UI Mask

-

Container Developers have the opportunity to customize some user interface (UI) elements which propagate to the App Developers' toolkit in F2.js. One of those is F2.UI.Mask. The Mask object contains configuration defaults for the F2.UI.showMask() and F2.UI.hideMask() methods.

-

An example of setting the mask in F2.init():

-
$(document).ready(function(){
-    F2.init({
-        //define ContainerConfig properties
-        appRender: function(appConfig, html){ ... },
-        beforeAppRender: function(appConfig, html){ ... },
-        afterAppRender: function(appConfig){ ... },
+

The web page and pre-loaded news app is a fully F2-enabled container. Rejoice!

+
+
+
+

Combining AppConfig and AppManifest

+

Container Developers can use the AppConfig and pre-loaded AppManifest (from step 2 above) in conjunction with F2.registerApps() to speed up the loading of F2 containers. The F2.registerApps() API supports two arguments: appConfigs and appManifests. The former is an array of F2.AppConfig objects and the latter is an array of F2.AppManifest objects. The appManifests array must be the same length as the appConfigs array that is used as the first argument. This can be useful if apps are loaded on the server-side and passed down to the client.

+

In the following example, the AppManifest was pre-loaded and stored in the _appManifest variable.

+
var _appConfig = {
+    appId: 'com_openf2_examples_csharp_marketnews',
+    description: 'Example News',
+    manifestUrl: 'http://www.openf2.org/Examples/Apps',
+    name: 'Example News',
+    root: document.getElementById('news_app')
+};
 
-        //setup UI
-        UI:{
-            Mask:{
-                loadingIcon:'./img/spinner.gif',
-                backgroundColor: '#fff',
-                opacity: 0.5
-            }
-        }
-    });
+var _appManifest = {
+   "apps":[{
+         "data":{},
+         "html": "<div data-module-name=\"MarketNewsApp\">...</div>",
+    }],
+   "scripts":[
+      "http://www.openf2.org/js/main.js"
+   ],
+   "styles":[
+      "http://www.openf2.org/css/site.css"
+   ]
+};
+
+$(function(){
+    F2.init();
+    F2.registerApps(_appConfig,_appManifest);
 });
-

Included in the F2.UI.Mask configuration object are the following properties: backgroundColor, loadingIcon, opacity, useClasses, and zIndex. Each of these F2.UI.Mask properties is detailed in the F2.js SDK docs.

-

For more information on F2.UI, browse to the F2.js SDK docs.

+

Important The F2.registerApps() API supports both an array of objects and object literals for each argument. Internally, F2.js converts the value of each argument into an array using concatenation ([].concat()). If arrays of objects are used (when there are more than one app on the container), the _appConfig and _appManifest arrays must be of equal length, and the object at each index must be a parallel reference. This means the AppConfig and AppManifest for the sample news app used above must be in _appConfig[0] and _appManifest[0].

+
-
-

Container Templates

-

If you're looking for sample container HTML template code, jump to the Get Started section. There is also a basic F2 container/app template available for download on GitHub.

+
+
+

AppHandlers for App Layout

+

New functionality called F2.AppHandlers was added in F2 1.2, and the conversation about this collection of features occurred in #38 on GitHub. The new AppHandlers functionality provides Container Developers a higher level of control over configuring app rendering and interaction.

+

+The addition of F2.AppHandlers replaces the previous ContainerConfig properties beforeAppRender, appRender, and afterAppRender. These methods were deprecated—but not removed—in version 1.2. They will be permanently removed in a future version of F2. +

+ +

+Starting with F2 version 1.2, AppHandlers is the preferred method for Container Developers to manage app layout. +

+ +

The AppHandlers functionality provides an event-based system for Container Developers' web applications. The addition of a collection of constants in F2.Constants.AppHandlers shows the primitive set of event types (or hooks) available to developers, including hooks such as appCreateRoot, appRenderAfter, appDestroyAfter and more. (Review the complete F2.Constants.AppHandlers collection in the F2.js SDK documentation.)

+

Using AppHandlers is as simple as attaching an event handler function to be executed at the appropriate time as determined by the order of operations in F2. To do this there are three functions available on F2.AppHandlers: getToken, on, and off. We'll review the token concept first as a token is the required first argument in on and off.

+
+

AppHandler Tokens

+

A new feature has been added to F2 as part of AppHandlers: the event token. The token is designed to be used only by Container Developers to ensure the AppHandlers listeners are only called by their applications, and aren't accessible to App Developers' code. Container Developers should create a variable for this token in their JavaScript and encapsulate it inside a closure as shown in the example below.

+
(function(){
+    var token = F2.AppHandlers.getToken(); 
+    console.log(token);
+    //outputs a GUID like 'ce2e7aae-04fa-96a3-edd7-be67e99937b4'
+});
+

Important The getToken() function can only be called one-time. It self-destructs to protect the token for Container Developers and therefore Container Developers must call F2.AppHandlers.getToken() and store its return value before any F2 apps are registered with the container.

+
+
+

Default App Layout

+

In the unlikely event a Container Developer wishes to append all apps to the <body> element, no configuration is required. Simply add this code to the container:

+
F2.init();
+F2.registerApps(appConfig);
+

Appending apps to the <body> is the default app rendering behavior of F2.

+
+
+

Custom App Layout

+

F2 AppHandlers provide event handlers for customized app layout using F2.AppHandlers.on() and F2.AppHandlers.off(). The use of on and off require both a token and an event type as arguments. The event types, defined as constants in F2.Constants.AppHandlers, are:

+
    +
  • appCreateRoot
  • +
  • appDestroy
  • +
  • appDestroyAfter
  • +
  • appDestroyBefore
  • +
  • appRender
  • +
  • appRenderAfter
  • +
  • appRenderBefore
  • +
+

Review the complete F2.Constants.AppHandlers collection and their purpose in the F2.js SDK documentation. The order of operations is detailed in F2.AppHandlers.

+
+

Appending an App to a DOM Element

+

There are many uses for AppHandlers in Container Developers' applications and they are detailed—including plenty of examples—in the F2.js SDK documentation. Before jumping to that section of the docs, let's look at one of the more common uses for AppHandlers: targeting the placement of an app into a specific DOM element.

+

In the following example, the app will be appended to the #my_sidebar DOM element on the container.

+
var _token = F2.AppHandlers.getToken(),
+    _appConfig = {
+        appId: 'com_example_app',
+        manifestUrl: '/manifest.js'
+    };
+
+F2.init();
+F2.AppHandlers.on(_token, 'appRender', document.getElementById('my_sidebar'));
+F2.registerApps(_appConfig);
+

F2 will insert html from the AppManifest inside the specified DOM element. The resulting HTML will look like this after registerApps is called. Take note F2.js adds three class names to the apps' outermost element (f2-app, f2-app-container, and com_example_app for the appId).

+
<div id="my_sidebar">
+    <!--HTML defined in AppManifest inserted here-->
+    <div class="f2-app f2-app-container com_example_app">
+        <div class="f2-app-view" data-f2-view="home">
+            <p>Hello World!</p>
+        </div>
+    </div>
+</div>
+

Note The original html in this example app manifest is available here.

+

The jsfiddle below demonstrates a Hello World example using the appRender event type and a DOM element as the third argument in on.

+ + +
+
+

Placing Apps in Separate Locations

+

Here is a slightly more complicated example of the appRender event coupled with appCreateRoot to place two apps in two separate DOM elements.

+ + +
+
+

More AppHandlers

+

There are numerous examples shown on the Properties tab of F2.Constants.AppHandlers. These demonstrate more advanced use of F2.AppHandlers and aim to provide Container Developers demonstrable low-level control over the life-cycle of app rendering.


@@ -430,7 +708,7 @@

Container-to-App Context

F2.Events.on(
     F2.Constants.Events.CONTAINER_SYMBOL_CHANGE, 
     function(data){
-        F2.log("The symbol was changed to " + data.symbol);
+        F2.log("The symbol was changed to " + data.symbol);
     }
 );

The F2.Events.on() method accepts the event name and listener function as arguments. Read the SDK for more information.

@@ -472,7 +750,7 @@

App-to-Container Context

F2.Events.on(
     F2.Constants.Events.APP_SYMBOL_CHANGE, 
     function(data){
-        F2.log("The symbol was changed to " + data.symbol);
+        F2.log("The symbol was changed to " + data.symbol);
     }
 );

Note For a full list of support event types, browse to the SDK for F2.Constants.Events.

@@ -495,8 +773,8 @@

App-to-App Context

F2.Events.on(
     "buy_stock", 
     function(data){
-        if (data.isAvailableToPurchase){
-            F2.log("Trade ticket order for " + data.symbol + " at $" + data.price);
+        if (data.isAvailableToPurchase){
+            F2.log("Trade ticket order for " + data.symbol + " at $" + data.price);
         } else {
             F2.log("This stock is not available for purchase.")
         }
@@ -548,7 +826,7 @@ 

More Complex Context

F2.Events.on(
     "buy_stock", 
     function(data){
-        F2.log("Trade ticket order for " + data.symbol + " at $" + data.price);
+        F2.log("Trade ticket order for " + data.symbol + " at $" + data.price);
         //..populate the trade ticket...
         //fire the callback
         if (typeof data.callback === 'function'){
@@ -567,127 +845,7 @@ 

Universal F2 Instrument ID

-
-

App Integration

-

The process of loading apps on a container happens through a method called F2.registerApps(). The Container Developer must call this method—which accepts two arguments, one required, one optional— after F2.init() is called. If this method isn't called, no apps can be loaded on the container.

-

The two arguments provided to registerApps() are an array of AppConfig objects and, optionally, an array of AppManifest objects. As F2.js parses each AppConfig, the apps are validated, hydrated with some additional properties, and saved in F2 memory on the container.

-

Regardless of where the container's AppConfig comes from, integrating apps is a simple process. For the purposes of this example, we will use an Acme Corp news app.

-

Let's look at some container code.

-
-

Static App Configuration

-

First, we define the AppConfigs in a hard-coded _appConfigs array. Secondly, when the document is ready, we call F2.init() and subsequently F2.registerApps() with the single argument.

-
//define app config
-var _appConfigs = [
-    {
-        appId: "com_acmecorp_news",
-        description: "Acme Corp News",
-        manifestUrl: "http://www.acme.com/apps/news-manifest.js",
-        name: "Acme News App"
-    }
-];
-
-$(document).ready(function(){
-
-    //init F2 container
-    F2.init({
-        //define ContainerConfig properties
-        appRender: function(appConfig, html){ ... },
-        beforeAppRender: function(appConfig, html){ ... },
-        afterAppRender: function(appConfig){ ... },
-
-        //setup UI
-        UI:{
-            Mask:{
-                loadingIcon:'./img/spinner.gif',
-                backgroundColor: '#fff',
-                opacity: 0.5
-            }
-        }
-    });
-
-    //load apps
-    F2.registerApps(_appConfigs);
-
-});
-

This javascript code will insert the Acme Corp news app into the container's DOM, provided the appRender method is configured correctly.

-
-
-

Dynamic App Configuration

-

Alternatively, AppConfigs could live in a database—eventually the F2 Store—at which time container developers could provide their containers with AppManifests instead of relying on each AppConfig.manifestUrl property to be retrieved and parsed at run time.

-

Such an implementation would require the container developer to make a HTTP call to a Store web service to retrieve AppConfigs and AppManifests. You are already familiar with what the AppConfig looks like, but if you aren't sure what an AppManifest looks like, take note of this empty manifest.

-
{
-    "inlineScripts":[],  
-    "scripts":[],    
-    "styles":[],     
-    "apps":[{
-            "data":{},
-            "html":"",
-            "status":""
-    }]
-}
-

Note Read more about the AppManifest.

-

An example of a container making a request to the F2 Store for AppConfigs and AppManifests:

-
(function(){
-    
-    var _appConfigs = [], _appManifests = [];
-
-    //make request to Store web service
-    var $req = $.ajax({
-        url: 'https://store.openf2.org/getApps',
-        dataType: 'jsonp'
-    });
-
-    //parse successful response
-    $req.done(function(jqxhr,txtStatus){
-        jqxhr = jqxhr || {};
-        if (jqxhr.status == "good"){
-            _appConfigs = jqxhr.appConfigs || [];
-            _appManifests = jqxhr.appManifests || [];
-            //load
-            loadContainer();
-        } else {
-            F2.log("Store web service did not do something 'good'.", jqxhr, txtStatus);
-        }
-    });
-
-    //handle errors
-    $req.fail(function(jqxhr,txtStatus){
-        F2.log("Store web service failed.", jqxhr, txtStatus);
-    });
-
-    //wrap this up so we can call it in $req.done()
-    var loadContainer = function(){
-        $(document).ready(function(){
-            //init F2 container
-            F2.init({
-                //define ContainerConfig properties
-                appRender: function(appConfig, html){ ... },
-                beforeAppRender: function(appConfig, html){ ... },
-                afterAppRender: function(appConfig){ ... },
-
-                //setup UI
-                UI:{
-                    Mask:{
-                        loadingIcon:'./img/spinner.gif',
-                        backgroundColor: '#fff',
-                        opacity: 0.5
-                    }
-                }
-            });
-
-            //load apps
-            F2.registerApps(_appConfigs, _appManifests);
-
-        });
-    }//loadContainer
-    
-})();
-

Important The _appConfigs and _appManifests arrays must be of equal length, and the object at each index must be a parallel reference. This means the AppConfig and AppManifest for Acme Corp's news app must be in _appConfigs[0] and _appManifests[0].

-

There are numerous benefits to dynamic app configuration, most notably performance and security. In the dynamic model, AppManifests have already been requested and loaded before a user opens the container reducing the overall number of outbound HTTP requests. Security is improved because Container Developers have the opportunity to parse and scrub AppManifest contents before F2.js injects markup in the AppManifest.html property into the container DOM.

-
-
-
-
+

Secure Apps

Security is a fundamental requirement of any F2 container and many F2 apps. With that in mind, the integration of secure apps on a container requires more attention and effort. The process of app integration remains largely the same for integrating secure apps with one significant addition: a second container.

To support a secured container environment, one of the choices made when writing this specification was the inclusion of an open-source cross-domain in-browser secure messaging library. For this, F2 relies on easyXDM. EasyXDM helps front-end developers safely work around the Same Origin Policy using browser-supported techniques without compromising the user experience. For all browsers, the easyXDM transport stack offers bi-directionality, reliability, queueing and sender-verification.

@@ -775,8 +933,8 @@

Considerations


@@ -784,8 +942,7 @@

Considerations

- - - + + \ No newline at end of file diff --git a/docs/css/F2.Docs.css b/docs/css/F2.Docs.css index b47b0196..be24fd5e 100644 --- a/docs/css/F2.Docs.css +++ b/docs/css/F2.Docs.css @@ -312,6 +312,7 @@ code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}pre.prettyprint{margin-bottom:20px;} pre code{padding:0;color:inherit;background-color:transparent;border:0;} .pre-scrollable{max-height:340px;overflow-y:scroll;} +a code{border-color:#e1e1e8 !important;} form{margin:0 0 20px;} fieldset{padding:0;margin:0;border:0;} legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333333;border:0;border-bottom:1px solid #e5e5e5;}legend small{font-size:15px;color:#999999;} diff --git a/docs/css/F2.Sdk.css b/docs/css/F2.Sdk.css index 7f89b22d..c1257ed8 100644 --- a/docs/css/F2.Sdk.css +++ b/docs/css/F2.Sdk.css @@ -312,6 +312,7 @@ code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}pre.prettyprint{margin-bottom:20px;} pre code{padding:0;color:inherit;background-color:transparent;border:0;} .pre-scrollable{max-height:340px;overflow-y:scroll;} +a code{border-color:#e1e1e8 !important;} form{margin:0 0 20px;} fieldset{padding:0;margin:0;border:0;} legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333333;border:0;border-bottom:1px solid #e5e5e5;}legend small{font-size:15px;color:#999999;} diff --git a/docs/css/F2.css b/docs/css/F2.css index 3db675bf..4020beba 100644 --- a/docs/css/F2.css +++ b/docs/css/F2.css @@ -312,6 +312,7 @@ code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}pre.prettyprint{margin-bottom:20px;} pre code{padding:0;color:inherit;background-color:transparent;border:0;} .pre-scrollable{max-height:340px;overflow-y:scroll;} +a code{border-color:#e1e1e8 !important;} form{margin:0 0 20px;} fieldset{padding:0;margin:0;border:0;} legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333333;border:0;border-bottom:1px solid #e5e5e5;}legend small{font-size:15px;color:#999999;} diff --git a/docs/extending-f2.html b/docs/extending-f2.html index 971a98b4..8e7021ab 100644 --- a/docs/extending-f2.html +++ b/docs/extending-f2.html @@ -3,10 +3,9 @@ F2 - Extending F2 - - + - + @@ -17,7 +16,7 @@ - + @@ -26,7 +25,7 @@ - +
@@ -94,19 +93,19 @@
- + @@ -119,8 +118,8 @@
-
-
+
+

Extending F2

@@ -183,8 +182,8 @@

Forum


@@ -192,8 +191,7 @@

Forum

- - - + + \ No newline at end of file diff --git a/docs/f2js-sdk.html b/docs/f2js-sdk.html index c401b24b..fdf53f02 100644 --- a/docs/f2js-sdk.html +++ b/docs/f2js-sdk.html @@ -3,10 +3,9 @@ F2 - F2.js SDK - - + - + @@ -17,7 +16,7 @@ - + @@ -26,7 +25,7 @@ - +
@@ -94,19 +93,19 @@
- + @@ -119,8 +118,8 @@
-
-
+
+

F2.js SDK

@@ -130,34 +129,51 @@

F2.js SDK

Developers who adhere to the F2 standard will make it possible for multiple apps, developed independently by different organizations, to function together creating a seamless and integrated experience.


-
-

The JavaScript

-

F2 is an open framework meaning anyone can build individual components or the entire product. To get Container and App Developers started, there is a JavaScript SDK—called F2.js—in addition to example apps as part of an open-source project maintained on GitHub.

+
+

F2.js

+

F2 is an open framework and to get Container and App Developers started, there is a JavaScript SDK—called F2.js—in addition to example apps as part of an open-source project maintained on GitHub.


Download

Anyone is free to download F2.js from the F2 project repository on GitHub. Once downloaded, F2.js can be added to any web page using a script tag:

<script src="/path/to/your/container/f2.js"></script>
-

The latest version of F2.js is 1.1.2.

+

The latest version of F2.js is 1.2.0.

-Download F2.js 1.1.2 View on GitHub +Download F2.js 1.2.0 View on GitHub


Versioning

-

The latest version of F2.js will always be in the project root, and the version number is in the top of file. The version number is also available on the command line by using node build -v.

+

The latest version of F2.js will always be in the project root, and the version number is in the top of file. The version number is also available on the command line by using:

+

$> grunt version.

To adhere to industry standards, F2 will be maintained under the Semantic Versioning guidelines as much as possible.

Releases will be numbered with the following format:

<major>.<minor>.<patch>

For more information on SemVer, please visit SemVer.org.


+
+

Upgrading

+

It is a goal of ours to make upgrading to the latest version of F2 a minor effort for development teams. Releasing feature enhancements, addressing bugs or security patches, and working hard to maintain backward compatibility for F2.js APIs—while constantly pushing the boundaries—are all part of evolving a web framework for the financial services industry.

+

The details from each release of F2, minor and major, are tracked in the changelog.

+
+

The Latest

+

For developers, getting the latest F2.js SDK is as simple as going to the project repository on GitHub. A download (zip) of the current version (1.2.0) of F2 is always available along with tags of previous releases.

+

Developers can quick-link to the latest copy of F2.js:

+

https://raw.github.com/OpenF2/F2/master/f2.js

+

Note GitHub is not a CDN.

+
+
+

Deprecated Features or APIs

+

As F2 features and/or F2.js APIs are deprecated, advance notice will be provided on any or all of the F2 communication channels. In addition, backward compatibility will be maintained for at least one minor version of F2. For example, if Feature X is deprecated in 1.0, backward compatibility will be maintained until at least version 1.1. F2 documentation will be updated accordingly to reflect any changes, and the conversation behind deprecated features will be publicly available on GitHub.

+
+

Forum

-

Have a question? Ask it on the F2 Google Group.

+

Have a question? Ask it on the F2 Google Group or start a discussion using Issues on GitHub.

- - - + + \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index f10ae011..8d456df9 100644 --- a/docs/index.html +++ b/docs/index.html @@ -6,10 +6,9 @@ - - + - + @@ -20,7 +19,7 @@ - + @@ -29,7 +28,7 @@ - +

@@ -97,19 +96,19 @@
- + @@ -122,8 +121,8 @@
-
-
+
+

About F2

@@ -224,8 +223,8 @@

Spec Management

.

Track

-

F2 v1.0 was released on October 15, 2012. The latest version of the F2 specification is 1.1.2 released on 8 April 2013. To provide transparency into the future of F2, a roadmap wiki will be available on GitHub. A changelog that tracks version-to-version changes, upgrades and deprecated features will offer a historical look at F2's evolution.

-

Note There is a separate changelog for the F2.js SDK which is currently version 1.1.2.

+

F2 v1.0 was released on October 15, 2012. The latest version of the F2 specification is 1.2.0 released on 7 June 2013. To provide transparency into the future of F2, a roadmap wiki will be available on GitHub. A changelog that tracks version-to-version changes, upgrades and deprecated features will offer a historical look at F2's evolution.

+

Note There is a separate changelog for the F2.js SDK which is currently version 1.2.0.

Collaborate

@@ -239,7 +238,7 @@

Collaborate

Notational Conventions

-

The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

+

The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119. For readability, these words do not appear in all uppercase letters in this specification.


@@ -249,10 +248,10 @@

Framework

Following are definitions for the main F2 Framework components: the apps, the container, and the store.

Choices

-

In order to ensure that applications built using F2 are successful, they must be accessible. With this in mind, the front-end technology choice is HTML5. Using the progressive enhancement methodology, F2 incorporates a rock-solid foundation. The F2 open standard provides guidelines for developers to add feature enhancements targeting specific environments or visitors. For example, F2 apps built following the mobile first design approach and with responsive CSS, allow users to access the apps on their desktop, tablet or smartphone and App Developers only need to build a single app.

+

In order to ensure that applications built using F2 are successful, they must be accessible. With this in mind, the front-end technology choice is HTML5. Using the progressive enhancement methodology, F2 incorporates a rock-solid foundation. The F2 open standard provides guidelines for developers to add feature enhancements targeting specific environments or visitors. For example, F2 apps built following the mobile first design approach and with responsive CSS, allow users to access the apps on their desktop, tablet or smartphone and App Developers only need to build a single app.

Support across all desktop browsers and mobile devices is sometimes limited so F2 includes some third-party web development libraries to bridge those gaps. Why reinvent the wheel, right?

    -
  • F2 uses and recommends Twitter Bootstrap for a consistent HTML & CSS structure for app development regardless of App Developer (we'll explain more later).
  • +
  • F2 uses and recommends Bootstrap for a consistent HTML & CSS structure for app development regardless of App Developer (we'll explain more later).
  • F2 relies on data structures represented in JSON, so it incorporates Crockford's JSON.
  • To support a secured container environment, F2 needs cross-domain in-browser messaging. For this, it relies on easyXDM.
@@ -373,8 +372,8 @@

Get Started


@@ -382,8 +381,7 @@

Get Started

- - - + + \ No newline at end of file diff --git a/docs/js/docs.js b/docs/js/docs.js index 75dbfe72..c5dfa110 100644 --- a/docs/js/docs.js +++ b/docs/js/docs.js @@ -30,10 +30,11 @@ F2Docs.fn.init = function() { this.buildBookmarks(); this.formatSourceCodeElements(); - $("body").scrollspy(); + /* removed b/c this removes 'active' class on top-level "Docs" nav
  • . weird. + $('body').scrollspy();*/ //affix left nav - $("#toc > ul.nav").affix(); + $('#toc > ul.nav').affix(); } /** @@ -285,11 +286,17 @@ F2Docs.fn._animateAnchor = function(e, isTableOfContentsLink){ */ F2Docs.fn._watchScrollSpy = function(){ $(window).on('scroll',$.proxy(function(e){ - var $nav = this.$currentSectionNavList; - var $activeNav = ("development" == this.currentPage) ? $nav.parent() : $('li',$nav).first(); - if (document.body.scrollTop < 1 && !$activeNav.hasClass('active')){ + var $nav = this.$currentSectionNavList, + $activeNav = $.proxy(function(){ + if (!this.activeNavElement){ + this.activeNavElement = ('development' == this.currentPage) ? $nav.parent() : $('li',$nav).first(); + } + return this.activeNavElement; + },this); + + if (document.body.scrollTop < 1 && !$activeNav().hasClass('active')){ $('li',$nav).removeClass('active'); - $activeNav.addClass('active'); + $activeNav().addClass('active'); } },this)); } @@ -337,7 +344,7 @@ F2Docs.fn.formatSourceCodeElements = function(){ } F2Docs.fn.insite = function(){ - if (F2.gitbranch() !== 'master') { return; } + if (!(/docs.openf2.org/i).test(window.location.hostname)) { return; } window._waq = window._waq || []; (function() { var domain = 'insite.wallst.com'; diff --git a/docs/js/f2.js b/docs/js/f2.js index 528c7b4f..167fc8de 100644 --- a/docs/js/f2.js +++ b/docs/js/f2.js @@ -1,11 +1,4 @@ -;(function(exports) { - - if (exports.F2 && !exports.F2_TESTING_MODE) { - return; - } -; - -/*! +(function(exports){if(!exports.F2||exports.F2_TESTING_MODE){/*! json2.js 2012-10-08 @@ -15,12 +8,28 @@ See http://www.JSON.org/js.html */ -;typeof JSON!="object"&&(JSON={}),function(){"use strict";function f(e){return e<10?"0"+e:e}function quote(e){return escapable.lastIndex=0,escapable.test(e)?'"'+e.replace(escapable,function(e){var t=meta[e];return typeof t=="string"?t:"\\u"+("0000"+e.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+e+'"'}function str(e,t){var n,r,i,s,o=gap,u,a=t[e];a&&typeof a=="object"&&typeof a.toJSON=="function"&&(a=a.toJSON(e)),typeof rep=="function"&&(a=rep.call(t,e,a));switch(typeof a){case"string":return quote(a);case"number":return isFinite(a)?String(a):"null";case"boolean":case"null":return String(a);case"object":if(!a)return"null";gap+=indent,u=[];if(Object.prototype.toString.apply(a)==="[object Array]"){s=a.length;for(n=0;n=0===n})}function lt(e){var t=ct.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function At(e,t){if(t.nodeType!==1||!v.hasData(e))return;var n,r,i,s=v._data(e),o=v._data(t,s),u=s.events;if(u){delete o.handle,o.events={};for(n in u)for(r=0,i=u[n].length;r").appendTo(i.body),n=t.css("display");t.remove();if(n==="none"||n===""){Pt=i.body.appendChild(Pt||v.extend(i.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!Ht||!Pt.createElement)Ht=(Pt.contentWindow||Pt.contentDocument).document,Ht.write(""),Ht.close();t=Ht.body.appendChild(Ht.createElement(e)),n=Dt(t,"display"),i.body.removeChild(Pt)}return Wt[e]=n,n}function fn(e,t,n,r){var i;if(v.isArray(t))v.each(t,function(t,i){n||sn.test(e)?r(e,i):fn(e+"["+(typeof i=="object"?t:"")+"]",i,n,r)});else if(!n&&v.type(t)==="object")for(i in t)fn(e+"["+i+"]",t[i],n,r);else r(e,t)}function Cn(e){return function(t,n){typeof t!="string"&&(n=t,t="*");var r,i,s,o=t.toLowerCase().split(y),u=0,a=o.length;if(v.isFunction(n))for(;u)[^>]*$|#([\w\-]*)$)/,E=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,S=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,T=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,N=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,C=/^-ms-/,k=/-([\da-z])/gi,L=function(e,t){return(t+"").toUpperCase()},A=function(){i.addEventListener?(i.removeEventListener("DOMContentLoaded",A,!1),v.ready()):i.readyState==="complete"&&(i.detachEvent("onreadystatechange",A),v.ready())},O={};v.fn=v.prototype={constructor:v,init:function(e,n,r){var s,o,u,a;if(!e)return this;if(e.nodeType)return this.context=this[0]=e,this.length=1,this;if(typeof e=="string"){e.charAt(0)==="<"&&e.charAt(e.length-1)===">"&&e.length>=3?s=[null,e,null]:s=w.exec(e);if(s&&(s[1]||!n)){if(s[1])return n=n instanceof v?n[0]:n,a=n&&n.nodeType?n.ownerDocument||n:i,e=v.parseHTML(s[1],a,!0),E.test(s[1])&&v.isPlainObject(n)&&this.attr.call(e,n,!0),v.merge(this,e);o=i.getElementById(s[2]);if(o&&o.parentNode){if(o.id!==s[2])return r.find(e);this.length=1,this[0]=o}return this.context=i,this.selector=e,this}return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e)}return v.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),v.makeArray(e,this))},selector:"",jquery:"1.8.3",length:0,size:function(){return this.length},toArray:function(){return l.call(this)},get:function(e){return e==null?this.toArray():e<0?this[this.length+e]:this[e]},pushStack:function(e,t,n){var r=v.merge(this.constructor(),e);return r.prevObject=this,r.context=this.context,t==="find"?r.selector=this.selector+(this.selector?" ":"")+n:t&&(r.selector=this.selector+"."+t+"("+n+")"),r},each:function(e,t){return v.each(this,e,t)},ready:function(e){return v.ready.promise().done(e),this},eq:function(e){return e=+e,e===-1?this.slice(e):this.slice(e,e+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(l.apply(this,arguments),"slice",l.call(arguments).join(","))},map:function(e){return this.pushStack(v.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:[].sort,splice:[].splice},v.fn.init.prototype=v.fn,v.extend=v.fn.extend=function(){var e,n,r,i,s,o,u=arguments[0]||{},a=1,f=arguments.length,l=!1;typeof u=="boolean"&&(l=u,u=arguments[1]||{},a=2),typeof u!="object"&&!v.isFunction(u)&&(u={}),f===a&&(u=this,--a);for(;a0)return;r.resolveWith(i,[v]),v.fn.trigger&&v(i).trigger("ready").off("ready")},isFunction:function(e){return v.type(e)==="function"},isArray:Array.isArray||function(e){return v.type(e)==="array"},isWindow:function(e){return e!=null&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return e==null?String(e):O[h.call(e)]||"object"},isPlainObject:function(e){if(!e||v.type(e)!=="object"||e.nodeType||v.isWindow(e))return!1;try{if(e.constructor&&!p.call(e,"constructor")&&!p.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||p.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw new Error(e)},parseHTML:function(e,t,n){var r;return!e||typeof e!="string"?null:(typeof t=="boolean"&&(n=t,t=0),t=t||i,(r=E.exec(e))?[t.createElement(r[1])]:(r=v.buildFragment([e],t,n?null:[]),v.merge([],(r.cacheable?v.clone(r.fragment):r.fragment).childNodes)))},parseJSON:function(t){if(!t||typeof t!="string")return null;t=v.trim(t);if(e.JSON&&e.JSON.parse)return e.JSON.parse(t);if(S.test(t.replace(T,"@").replace(N,"]").replace(x,"")))return(new Function("return "+t))();v.error("Invalid JSON: "+t)},parseXML:function(n){var r,i;if(!n||typeof n!="string")return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(s){r=t}return(!r||!r.documentElement||r.getElementsByTagName("parsererror").length)&&v.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&g.test(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(C,"ms-").replace(k,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,n,r){var i,s=0,o=e.length,u=o===t||v.isFunction(e);if(r){if(u){for(i in e)if(n.apply(e[i],r)===!1)break}else for(;s0&&e[0]&&e[a-1]||a===0||v.isArray(e));if(f)for(;u-1)a.splice(n,1),i&&(n<=o&&o--,n<=u&&u--)}),this},has:function(e){return v.inArray(e,a)>-1},empty:function(){return a=[],this},disable:function(){return a=f=n=t,this},disabled:function(){return!a},lock:function(){return f=t,n||c.disable(),this},locked:function(){return!f},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],a&&(!r||f)&&(i?f.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},v.extend({Deferred:function(e){var t=[["resolve","done",v.Callbacks("once memory"),"resolved"],["reject","fail",v.Callbacks("once memory"),"rejected"],["notify","progress",v.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return v.Deferred(function(n){v.each(t,function(t,r){var s=r[0],o=e[t];i[r[1]](v.isFunction(o)?function(){var e=o.apply(this,arguments);e&&v.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===i?n:this,[e])}:n[s])}),e=null}).promise()},promise:function(e){return e!=null?v.extend(e,r):r}},i={};return r.pipe=r.then,v.each(t,function(e,s){var o=s[2],u=s[3];r[s[1]]=o.add,u&&o.add(function(){n=u},t[e^1][2].disable,t[2][2].lock),i[s[0]]=o.fire,i[s[0]+"With"]=o.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=l.call(arguments),r=n.length,i=r!==1||e&&v.isFunction(e.promise)?r:0,s=i===1?e:v.Deferred(),o=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?l.call(arguments):r,n===u?s.notifyWith(t,n):--i||s.resolveWith(t,n)}},u,a,f;if(r>1){u=new Array(r),a=new Array(r),f=new Array(r);for(;t
    a",n=p.getElementsByTagName("*"),r=p.getElementsByTagName("a")[0];if(!n||!r||!n.length)return{};s=i.createElement("select"),o=s.appendChild(i.createElement("option")),u=p.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:r.getAttribute("href")==="/a",opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:u.value==="on",optSelected:o.selected,getSetAttribute:p.className!=="t",enctype:!!i.createElement("form").enctype,html5Clone:i.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:i.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},u.checked=!0,t.noCloneChecked=u.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!o.disabled;try{delete p.test}catch(d){t.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",h=function(){t.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick"),p.detachEvent("onclick",h)),u=i.createElement("input"),u.value="t",u.setAttribute("type","radio"),t.radioValue=u.value==="t",u.setAttribute("checked","checked"),u.setAttribute("name","t"),p.appendChild(u),a=i.createDocumentFragment(),a.appendChild(p.lastChild),t.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,t.appendChecked=u.checked,a.removeChild(u),a.appendChild(p);if(p.attachEvent)for(l in{submit:!0,change:!0,focusin:!0})f="on"+l,c=f in p,c||(p.setAttribute(f,"return;"),c=typeof p[f]=="function"),t[l+"Bubbles"]=c;return v(function(){var n,r,s,o,u="padding:0;margin:0;border:0;display:block;overflow:hidden;",a=i.getElementsByTagName("body")[0];if(!a)return;n=i.createElement("div"),n.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",a.insertBefore(n,a.firstChild),r=i.createElement("div"),n.appendChild(r),r.innerHTML="
    t
    ",s=r.getElementsByTagName("td"),s[0].style.cssText="padding:0;margin:0;border:0;display:none",c=s[0].offsetHeight===0,s[0].style.display="",s[1].style.display="none",t.reliableHiddenOffsets=c&&s[0].offsetHeight===0,r.innerHTML="",r.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=r.offsetWidth===4,t.doesNotIncludeMarginInBodyOffset=a.offsetTop!==1,e.getComputedStyle&&(t.pixelPosition=(e.getComputedStyle(r,null)||{}).top!=="1%",t.boxSizingReliable=(e.getComputedStyle(r,null)||{width:"4px"}).width==="4px",o=i.createElement("div"),o.style.cssText=r.style.cssText=u,o.style.marginRight=o.style.width="0",r.style.width="1px",r.appendChild(o),t.reliableMarginRight=!parseFloat((e.getComputedStyle(o,null)||{}).marginRight)),typeof r.style.zoom!="undefined"&&(r.innerHTML="",r.style.cssText=u+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=r.offsetWidth===3,r.style.display="block",r.style.overflow="visible",r.innerHTML="
    ",r.firstChild.style.width="5px",t.shrinkWrapBlocks=r.offsetWidth!==3,n.style.zoom=1),a.removeChild(n),n=r=s=o=null}),a.removeChild(p),n=r=s=o=u=a=p=null,t}();var D=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;v.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(v.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?v.cache[e[v.expando]]:e[v.expando],!!e&&!B(e)},data:function(e,n,r,i){if(!v.acceptData(e))return;var s,o,u=v.expando,a=typeof n=="string",f=e.nodeType,l=f?v.cache:e,c=f?e[u]:e[u]&&u;if((!c||!l[c]||!i&&!l[c].data)&&a&&r===t)return;c||(f?e[u]=c=v.deletedIds.pop()||v.guid++:c=u),l[c]||(l[c]={},f||(l[c].toJSON=v.noop));if(typeof n=="object"||typeof n=="function")i?l[c]=v.extend(l[c],n):l[c].data=v.extend(l[c].data,n);return s=l[c],i||(s.data||(s.data={}),s=s.data),r!==t&&(s[v.camelCase(n)]=r),a?(o=s[n],o==null&&(o=s[v.camelCase(n)])):o=s,o},removeData:function(e,t,n){if(!v.acceptData(e))return;var r,i,s,o=e.nodeType,u=o?v.cache:e,a=o?e[v.expando]:v.expando;if(!u[a])return;if(t){r=n?u[a]:u[a].data;if(r){v.isArray(t)||(t in r?t=[t]:(t=v.camelCase(t),t in r?t=[t]:t=t.split(" ")));for(i=0,s=t.length;i1,null,!1))},removeData:function(e){return this.each(function(){v.removeData(this,e)})}}),v.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=v._data(e,t),n&&(!r||v.isArray(n)?r=v._data(e,t,v.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=v.queue(e,t),r=n.length,i=n.shift(),s=v._queueHooks(e,t),o=function(){v.dequeue(e,t)};i==="inprogress"&&(i=n.shift(),r--),i&&(t==="fx"&&n.unshift("inprogress"),delete s.stop,i.call(e,o,s)),!r&&s&&s.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return v._data(e,n)||v._data(e,n,{empty:v.Callbacks("once memory").add(function(){v.removeData(e,t+"queue",!0),v.removeData(e,n,!0)})})}}),v.fn.extend({queue:function(e,n){var r=2;return typeof e!="string"&&(n=e,e="fx",r--),arguments.length1)},removeAttr:function(e){return this.each(function(){v.removeAttr(this,e)})},prop:function(e,t){return v.access(this,v.prop,e,t,arguments.length>1)},removeProp:function(e){return e=v.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,s,o,u;if(v.isFunction(e))return this.each(function(t){v(this).addClass(e.call(this,t,this.className))});if(e&&typeof e=="string"){t=e.split(y);for(n=0,r=this.length;n=0)r=r.replace(" "+n[s]+" "," ");i.className=e?v.trim(r):""}}}return this},toggleClass:function(e,t){var n=typeof e,r=typeof t=="boolean";return v.isFunction(e)?this.each(function(n){v(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if(n==="string"){var i,s=0,o=v(this),u=t,a=e.split(y);while(i=a[s++])u=r?u:!o.hasClass(i),o[u?"addClass":"removeClass"](i)}else if(n==="undefined"||n==="boolean")this.className&&v._data(this,"__className__",this.className),this.className=this.className||e===!1?"":v._data(this,"__className__")||""})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;n=0)return!0;return!1},val:function(e){var n,r,i,s=this[0];if(!arguments.length){if(s)return n=v.valHooks[s.type]||v.valHooks[s.nodeName.toLowerCase()],n&&"get"in n&&(r=n.get(s,"value"))!==t?r:(r=s.value,typeof r=="string"?r.replace(R,""):r==null?"":r);return}return i=v.isFunction(e),this.each(function(r){var s,o=v(this);if(this.nodeType!==1)return;i?s=e.call(this,r,o.val()):s=e,s==null?s="":typeof s=="number"?s+="":v.isArray(s)&&(s=v.map(s,function(e){return e==null?"":e+""})),n=v.valHooks[this.type]||v.valHooks[this.nodeName.toLowerCase()];if(!n||!("set"in n)||n.set(this,s,"value")===t)this.value=s})}}),v.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,s=e.type==="select-one"||i<0,o=s?null:[],u=s?i+1:r.length,a=i<0?u:s?i:0;for(;a=0}),n.length||(e.selectedIndex=-1),n}}},attrFn:{},attr:function(e,n,r,i){var s,o,u,a=e.nodeType;if(!e||a===3||a===8||a===2)return;if(i&&v.isFunction(v.fn[n]))return v(e)[n](r);if(typeof e.getAttribute=="undefined")return v.prop(e,n,r);u=a!==1||!v.isXMLDoc(e),u&&(n=n.toLowerCase(),o=v.attrHooks[n]||(X.test(n)?F:j));if(r!==t){if(r===null){v.removeAttr(e,n);return}return o&&"set"in o&&u&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r)}return o&&"get"in o&&u&&(s=o.get(e,n))!==null?s:(s=e.getAttribute(n),s===null?t:s)},removeAttr:function(e,t){var n,r,i,s,o=0;if(t&&e.nodeType===1){r=t.split(y);for(;o=0}})});var $=/^(?:textarea|input|select)$/i,J=/^([^\.]*|)(?:\.(.+)|)$/,K=/(?:^|\s)hover(\.\S+|)\b/,Q=/^key/,G=/^(?:mouse|contextmenu)|click/,Y=/^(?:focusinfocus|focusoutblur)$/,Z=function(e){return v.event.special.hover?e:e.replace(K,"mouseenter$1 mouseleave$1")};v.event={add:function(e,n,r,i,s){var o,u,a,f,l,c,h,p,d,m,g;if(e.nodeType===3||e.nodeType===8||!n||!r||!(o=v._data(e)))return;r.handler&&(d=r,r=d.handler,s=d.selector),r.guid||(r.guid=v.guid++),a=o.events,a||(o.events=a={}),u=o.handle,u||(o.handle=u=function(e){return typeof v=="undefined"||!!e&&v.event.triggered===e.type?t:v.event.dispatch.apply(u.elem,arguments)},u.elem=e),n=v.trim(Z(n)).split(" ");for(f=0;f=0&&(y=y.slice(0,-1),a=!0),y.indexOf(".")>=0&&(b=y.split("."),y=b.shift(),b.sort());if((!s||v.event.customEvent[y])&&!v.event.global[y])return;n=typeof n=="object"?n[v.expando]?n:new v.Event(y,n):new v.Event(y),n.type=y,n.isTrigger=!0,n.exclusive=a,n.namespace=b.join("."),n.namespace_re=n.namespace?new RegExp("(^|\\.)"+b.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,h=y.indexOf(":")<0?"on"+y:"";if(!s){u=v.cache;for(f in u)u[f].events&&u[f].events[y]&&v.event.trigger(n,r,u[f].handle.elem,!0);return}n.result=t,n.target||(n.target=s),r=r!=null?v.makeArray(r):[],r.unshift(n),p=v.event.special[y]||{};if(p.trigger&&p.trigger.apply(s,r)===!1)return;m=[[s,p.bindType||y]];if(!o&&!p.noBubble&&!v.isWindow(s)){g=p.delegateType||y,l=Y.test(g+y)?s:s.parentNode;for(c=s;l;l=l.parentNode)m.push([l,g]),c=l;c===(s.ownerDocument||i)&&m.push([c.defaultView||c.parentWindow||e,g])}for(f=0;f=0:v.find(h,this,null,[s]).length),u[h]&&f.push(c);f.length&&w.push({elem:s,matches:f})}d.length>m&&w.push({elem:this,matches:d.slice(m)});for(r=0;r0?this.on(t,null,e,n):this.trigger(t)},Q.test(t)&&(v.event.fixHooks[t]=v.event.keyHooks),G.test(t)&&(v.event.fixHooks[t]=v.event.mouseHooks)}),function(e,t){function nt(e,t,n,r){n=n||[],t=t||g;var i,s,a,f,l=t.nodeType;if(!e||typeof e!="string")return n;if(l!==1&&l!==9)return[];a=o(t);if(!a&&!r)if(i=R.exec(e))if(f=i[1]){if(l===9){s=t.getElementById(f);if(!s||!s.parentNode)return n;if(s.id===f)return n.push(s),n}else if(t.ownerDocument&&(s=t.ownerDocument.getElementById(f))&&u(t,s)&&s.id===f)return n.push(s),n}else{if(i[2])return S.apply(n,x.call(t.getElementsByTagName(e),0)),n;if((f=i[3])&&Z&&t.getElementsByClassName)return S.apply(n,x.call(t.getElementsByClassName(f),0)),n}return vt(e.replace(j,"$1"),t,n,r,a)}function rt(e){return function(t){var n=t.nodeName.toLowerCase();return n==="input"&&t.type===e}}function it(e){return function(t){var n=t.nodeName.toLowerCase();return(n==="input"||n==="button")&&t.type===e}}function st(e){return N(function(t){return t=+t,N(function(n,r){var i,s=e([],n.length,t),o=s.length;while(o--)n[i=s[o]]&&(n[i]=!(r[i]=n[i]))})})}function ot(e,t,n){if(e===t)return n;var r=e.nextSibling;while(r){if(r===t)return-1;r=r.nextSibling}return 1}function ut(e,t){var n,r,s,o,u,a,f,l=L[d][e+" "];if(l)return t?0:l.slice(0);u=e,a=[],f=i.preFilter;while(u){if(!n||(r=F.exec(u)))r&&(u=u.slice(r[0].length)||u),a.push(s=[]);n=!1;if(r=I.exec(u))s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=r[0].replace(j," ");for(o in i.filter)(r=J[o].exec(u))&&(!f[o]||(r=f[o](r)))&&(s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=o,n.matches=r);if(!n)break}return t?u.length:u?nt.error(e):L(e,a).slice(0)}function at(e,t,r){var i=t.dir,s=r&&t.dir==="parentNode",o=w++;return t.first?function(t,n,r){while(t=t[i])if(s||t.nodeType===1)return e(t,n,r)}:function(t,r,u){if(!u){var a,f=b+" "+o+" ",l=f+n;while(t=t[i])if(s||t.nodeType===1){if((a=t[d])===l)return t.sizset;if(typeof a=="string"&&a.indexOf(f)===0){if(t.sizset)return t}else{t[d]=l;if(e(t,r,u))return t.sizset=!0,t;t.sizset=!1}}}else while(t=t[i])if(s||t.nodeType===1)if(e(t,r,u))return t}}function ft(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function lt(e,t,n,r,i){var s,o=[],u=0,a=e.length,f=t!=null;for(;u-1&&(s[f]=!(o[f]=c))}}else g=lt(g===o?g.splice(d,g.length):g),i?i(null,o,g,a):S.apply(o,g)})}function ht(e){var t,n,r,s=e.length,o=i.relative[e[0].type],u=o||i.relative[" "],a=o?1:0,f=at(function(e){return e===t},u,!0),l=at(function(e){return T.call(t,e)>-1},u,!0),h=[function(e,n,r){return!o&&(r||n!==c)||((t=n).nodeType?f(e,n,r):l(e,n,r))}];for(;a1&&ft(h),a>1&&e.slice(0,a-1).join("").replace(j,"$1"),n,a0,s=e.length>0,o=function(u,a,f,l,h){var p,d,v,m=[],y=0,w="0",x=u&&[],T=h!=null,N=c,C=u||s&&i.find.TAG("*",h&&a.parentNode||a),k=b+=N==null?1:Math.E;T&&(c=a!==g&&a,n=o.el);for(;(p=C[w])!=null;w++){if(s&&p){for(d=0;v=e[d];d++)if(v(p,a,f)){l.push(p);break}T&&(b=k,n=++o.el)}r&&((p=!v&&p)&&y--,u&&x.push(p))}y+=w;if(r&&w!==y){for(d=0;v=t[d];d++)v(x,m,a,f);if(u){if(y>0)while(w--)!x[w]&&!m[w]&&(m[w]=E.call(l));m=lt(m)}S.apply(l,m),T&&!u&&m.length>0&&y+t.length>1&&nt.uniqueSort(l)}return T&&(b=k,c=N),x};return o.el=0,r?N(o):o}function dt(e,t,n){var r=0,i=t.length;for(;r2&&(f=u[0]).type==="ID"&&t.nodeType===9&&!s&&i.relative[u[1].type]){t=i.find.ID(f.matches[0].replace($,""),t,s)[0];if(!t)return n;e=e.slice(u.shift().length)}for(o=J.POS.test(e)?-1:u.length-1;o>=0;o--){f=u[o];if(i.relative[l=f.type])break;if(c=i.find[l])if(r=c(f.matches[0].replace($,""),z.test(u[0].type)&&t.parentNode||t,s)){u.splice(o,1),e=r.length&&u.join("");if(!e)return S.apply(n,x.call(r,0)),n;break}}}return a(e,h)(r,t,s,n,z.test(e)),n}function mt(){}var n,r,i,s,o,u,a,f,l,c,h=!0,p="undefined",d=("sizcache"+Math.random()).replace(".",""),m=String,g=e.document,y=g.documentElement,b=0,w=0,E=[].pop,S=[].push,x=[].slice,T=[].indexOf||function(e){var t=0,n=this.length;for(;ti.cacheLength&&delete e[t.shift()],e[n+" "]=r},e)},k=C(),L=C(),A=C(),O="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",_=M.replace("w","w#"),D="([*^$|!~]?=)",P="\\["+O+"*("+M+")"+O+"*(?:"+D+O+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+_+")|)|)"+O+"*\\]",H=":("+M+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+P+")|[^:]|\\\\.)*|.*))\\)|)",B=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+O+"*((?:-\\d)?\\d*)"+O+"*\\)|)(?=[^-]|$)",j=new RegExp("^"+O+"+|((?:^|[^\\\\])(?:\\\\.)*)"+O+"+$","g"),F=new RegExp("^"+O+"*,"+O+"*"),I=new RegExp("^"+O+"*([\\x20\\t\\r\\n\\f>+~])"+O+"*"),q=new RegExp(H),R=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,U=/^:not/,z=/[\x20\t\r\n\f]*[+~]/,W=/:not\($/,X=/h\d/i,V=/input|select|textarea|button/i,$=/\\(?!\\)/g,J={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),NAME:new RegExp("^\\[name=['\"]?("+M+")['\"]?\\]"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+H),POS:new RegExp(B,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+O+"*(even|odd|(([+-]|)(\\d*)n|)"+O+"*(?:([+-]|)"+O+"*(\\d+)|))"+O+"*\\)|)","i"),needsContext:new RegExp("^"+O+"*[>+~]|"+B,"i")},K=function(e){var t=g.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}},Q=K(function(e){return e.appendChild(g.createComment("")),!e.getElementsByTagName("*").length}),G=K(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==p&&e.firstChild.getAttribute("href")==="#"}),Y=K(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return t!=="boolean"&&t!=="string"}),Z=K(function(e){return e.innerHTML="",!e.getElementsByClassName||!e.getElementsByClassName("e").length?!1:(e.lastChild.className="e",e.getElementsByClassName("e").length===2)}),et=K(function(e){e.id=d+0,e.innerHTML="
    ",y.insertBefore(e,y.firstChild);var t=g.getElementsByName&&g.getElementsByName(d).length===2+g.getElementsByName(d+0).length;return r=!g.getElementById(d),y.removeChild(e),t});try{x.call(y.childNodes,0)[0].nodeType}catch(tt){x=function(e){var t,n=[];for(;t=this[e];e++)n.push(t);return n}}nt.matches=function(e,t){return nt(e,null,null,t)},nt.matchesSelector=function(e,t){return nt(t,null,null,[e]).length>0},s=nt.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(i===1||i===9||i===11){if(typeof e.textContent=="string")return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=s(e)}else if(i===3||i===4)return e.nodeValue}else for(;t=e[r];r++)n+=s(t);return n},o=nt.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?t.nodeName!=="HTML":!1},u=nt.contains=y.contains?function(e,t){var n=e.nodeType===9?e.documentElement:e,r=t&&t.parentNode;return e===r||!!(r&&r.nodeType===1&&n.contains&&n.contains(r))}:y.compareDocumentPosition?function(e,t){return t&&!!(e.compareDocumentPosition(t)&16)}:function(e,t){while(t=t.parentNode)if(t===e)return!0;return!1},nt.attr=function(e,t){var n,r=o(e);return r||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):r||Y?e.getAttribute(t):(n=e.getAttributeNode(t),n?typeof e[t]=="boolean"?e[t]?t:null:n.specified?n.value:null:null)},i=nt.selectors={cacheLength:50,createPseudo:N,match:J,attrHandle:G?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},find:{ID:r?function(e,t,n){if(typeof t.getElementById!==p&&!n){var r=t.getElementById(e);return r&&r.parentNode?[r]:[]}}:function(e,n,r){if(typeof n.getElementById!==p&&!r){var i=n.getElementById(e);return i?i.id===e||typeof i.getAttributeNode!==p&&i.getAttributeNode("id").value===e?[i]:t:[]}},TAG:Q?function(e,t){if(typeof t.getElementsByTagName!==p)return t.getElementsByTagName(e)}:function(e,t){var n=t.getElementsByTagName(e);if(e==="*"){var r,i=[],s=0;for(;r=n[s];s++)r.nodeType===1&&i.push(r);return i}return n},NAME:et&&function(e,t){if(typeof t.getElementsByName!==p)return t.getElementsByName(name)},CLASS:Z&&function(e,t,n){if(typeof t.getElementsByClassName!==p&&!n)return t.getElementsByClassName(e)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace($,""),e[3]=(e[4]||e[5]||"").replace($,""),e[2]==="~="&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),e[1]==="nth"?(e[2]||nt.error(e[0]),e[3]=+(e[3]?e[4]+(e[5]||1):2*(e[2]==="even"||e[2]==="odd")),e[4]=+(e[6]+e[7]||e[2]==="odd")):e[2]&&nt.error(e[0]),e},PSEUDO:function(e){var t,n;if(J.CHILD.test(e[0]))return null;if(e[3])e[2]=e[3];else if(t=e[4])q.test(t)&&(n=ut(t,!0))&&(n=t.indexOf(")",t.length-n)-t.length)&&(t=t.slice(0,n),e[0]=e[0].slice(0,n)),e[2]=t;return e.slice(0,3)}},filter:{ID:r?function(e){return e=e.replace($,""),function(t){return t.getAttribute("id")===e}}:function(e){return e=e.replace($,""),function(t){var n=typeof t.getAttributeNode!==p&&t.getAttributeNode("id");return n&&n.value===e}},TAG:function(e){return e==="*"?function(){return!0}:(e=e.replace($,"").toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[d][e+" "];return t||(t=new RegExp("(^|"+O+")"+e+"("+O+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==p&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r,i){var s=nt.attr(r,e);return s==null?t==="!=":t?(s+="",t==="="?s===n:t==="!="?s!==n:t==="^="?n&&s.indexOf(n)===0:t==="*="?n&&s.indexOf(n)>-1:t==="$="?n&&s.substr(s.length-n.length)===n:t==="~="?(" "+s+" ").indexOf(n)>-1:t==="|="?s===n||s.substr(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r){return e==="nth"?function(e){var t,i,s=e.parentNode;if(n===1&&r===0)return!0;if(s){i=0;for(t=s.firstChild;t;t=t.nextSibling)if(t.nodeType===1){i++;if(e===t)break}}return i-=r,i===n||i%n===0&&i/n>=0}:function(t){var n=t;switch(e){case"only":case"first":while(n=n.previousSibling)if(n.nodeType===1)return!1;if(e==="first")return!0;n=t;case"last":while(n=n.nextSibling)if(n.nodeType===1)return!1;return!0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||nt.error("unsupported pseudo: "+e);return r[d]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?N(function(e,n){var i,s=r(e,t),o=s.length;while(o--)i=T.call(e,s[o]),e[i]=!(n[i]=s[o])}):function(e){return r(e,0,n)}):r}},pseudos:{not:N(function(e){var t=[],n=[],r=a(e.replace(j,"$1"));return r[d]?N(function(e,t,n,i){var s,o=r(e,null,i,[]),u=e.length;while(u--)if(s=o[u])e[u]=!(t[u]=s)}):function(e,i,s){return t[0]=e,r(t,null,s,n),!n.pop()}}),has:N(function(e){return function(t){return nt(e,t).length>0}}),contains:N(function(e){return function(t){return(t.textContent||t.innerText||s(t)).indexOf(e)>-1}}),enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&!!e.checked||t==="option"&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},parent:function(e){return!i.pseudos.empty(e)},empty:function(e){var t;e=e.firstChild;while(e){if(e.nodeName>"@"||(t=e.nodeType)===3||t===4)return!1;e=e.nextSibling}return!0},header:function(e){return X.test(e.nodeName)},text:function(e){var t,n;return e.nodeName.toLowerCase()==="input"&&(t=e.type)==="text"&&((n=e.getAttribute("type"))==null||n.toLowerCase()===t)},radio:rt("radio"),checkbox:rt("checkbox"),file:rt("file"),password:rt("password"),image:rt("image"),submit:it("submit"),reset:it("reset"),button:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&e.type==="button"||t==="button"},input:function(e){return V.test(e.nodeName)},focus:function(e){var t=e.ownerDocument;return e===t.activeElement&&(!t.hasFocus||t.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},active:function(e){return e===e.ownerDocument.activeElement},first:st(function(){return[0]}),last:st(function(e,t){return[t-1]}),eq:st(function(e,t,n){return[n<0?n+t:n]}),even:st(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:st(function(e,t,n){for(var r=n<0?n+t:n;++r",e.querySelectorAll("[selected]").length||i.push("\\["+O+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||i.push(":checked")}),K(function(e){e.innerHTML="

    ",e.querySelectorAll("[test^='']").length&&i.push("[*^$]="+O+"*(?:\"\"|'')"),e.innerHTML="",e.querySelectorAll(":enabled").length||i.push(":enabled",":disabled")}),i=new RegExp(i.join("|")),vt=function(e,r,s,o,u){if(!o&&!u&&!i.test(e)){var a,f,l=!0,c=d,h=r,p=r.nodeType===9&&e;if(r.nodeType===1&&r.nodeName.toLowerCase()!=="object"){a=ut(e),(l=r.getAttribute("id"))?c=l.replace(n,"\\$&"):r.setAttribute("id",c),c="[id='"+c+"'] ",f=a.length;while(f--)a[f]=c+a[f].join("");h=z.test(e)&&r.parentNode||r,p=a.join(",")}if(p)try{return S.apply(s,x.call(h.querySelectorAll(p),0)),s}catch(v){}finally{l||r.removeAttribute("id")}}return t(e,r,s,o,u)},u&&(K(function(t){e=u.call(t,"div");try{u.call(t,"[test!='']:sizzle"),s.push("!=",H)}catch(n){}}),s=new RegExp(s.join("|")),nt.matchesSelector=function(t,n){n=n.replace(r,"='$1']");if(!o(t)&&!s.test(n)&&!i.test(n))try{var a=u.call(t,n);if(a||e||t.document&&t.document.nodeType!==11)return a}catch(f){}return nt(n,null,null,[t]).length>0})}(),i.pseudos.nth=i.pseudos.eq,i.filters=mt.prototype=i.pseudos,i.setFilters=new mt,nt.attr=v.attr,v.find=nt,v.expr=nt.selectors,v.expr[":"]=v.expr.pseudos,v.unique=nt.uniqueSort,v.text=nt.getText,v.isXMLDoc=nt.isXML,v.contains=nt.contains}(e);var nt=/Until$/,rt=/^(?:parents|prev(?:Until|All))/,it=/^.[^:#\[\.,]*$/,st=v.expr.match.needsContext,ot={children:!0,contents:!0,next:!0,prev:!0};v.fn.extend({find:function(e){var t,n,r,i,s,o,u=this;if(typeof e!="string")return v(e).filter(function(){for(t=0,n=u.length;t0)for(i=r;i=0:v.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,s=[],o=st.test(e)||typeof e!="string"?v(e,t||this.context):0;for(;r-1:v.find.matchesSelector(n,e)){s.push(n);break}n=n.parentNode}}return s=s.length>1?v.unique(s):s,this.pushStack(s,"closest",e)},index:function(e){return e?typeof e=="string"?v.inArray(this[0],v(e)):v.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(e,t){var n=typeof e=="string"?v(e,t):v.makeArray(e&&e.nodeType?[e]:e),r=v.merge(this.get(),n);return this.pushStack(ut(n[0])||ut(r[0])?r:v.unique(r))},addBack:function(e){return this.add(e==null?this.prevObject:this.prevObject.filter(e))}}),v.fn.andSelf=v.fn.addBack,v.each({parent:function(e){var t=e.parentNode;return t&&t.nodeType!==11?t:null},parents:function(e){return v.dir(e,"parentNode")},parentsUntil:function(e,t,n){return v.dir(e,"parentNode",n)},next:function(e){return at(e,"nextSibling")},prev:function(e){return at(e,"previousSibling")},nextAll:function(e){return v.dir(e,"nextSibling")},prevAll:function(e){return v.dir(e,"previousSibling")},nextUntil:function(e,t,n){return v.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return v.dir(e,"previousSibling",n)},siblings:function(e){return v.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return v.sibling(e.firstChild)},contents:function(e){return v.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:v.merge([],e.childNodes)}},function(e,t){v.fn[e]=function(n,r){var i=v.map(this,t,n);return nt.test(e)||(r=n),r&&typeof r=="string"&&(i=v.filter(r,i)),i=this.length>1&&!ot[e]?v.unique(i):i,this.length>1&&rt.test(e)&&(i=i.reverse()),this.pushStack(i,e,l.call(arguments).join(","))}}),v.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),t.length===1?v.find.matchesSelector(t[0],e)?[t[0]]:[]:v.find.matches(e,t)},dir:function(e,n,r){var i=[],s=e[n];while(s&&s.nodeType!==9&&(r===t||s.nodeType!==1||!v(s).is(r)))s.nodeType===1&&i.push(s),s=s[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)e.nodeType===1&&e!==t&&n.push(e);return n}});var ct="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",ht=/ jQuery\d+="(?:null|\d+)"/g,pt=/^\s+/,dt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,vt=/<([\w:]+)/,mt=/]","i"),Et=/^(?:checkbox|radio)$/,St=/checked\s*(?:[^=]|=\s*.checked.)/i,xt=/\/(java|ecma)script/i,Tt=/^\s*\s*$/g,Nt={option:[1,""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]},Ct=lt(i),kt=Ct.appendChild(i.createElement("div"));Nt.optgroup=Nt.option,Nt.tbody=Nt.tfoot=Nt.colgroup=Nt.caption=Nt.thead,Nt.th=Nt.td,v.support.htmlSerialize||(Nt._default=[1,"X
    ","
    "]),v.fn.extend({text:function(e){return v.access(this,function(e){return e===t?v.text(this):this.empty().append((this[0]&&this[0].ownerDocument||i).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(v.isFunction(e))return this.each(function(t){v(this).wrapAll(e.call(this,t))});if(this[0]){var t=v(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&e.firstChild.nodeType===1)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return v.isFunction(e)?this.each(function(t){v(this).wrapInner(e.call(this,t))}):this.each(function(){var t=v(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=v.isFunction(e);return this.each(function(n){v(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){v.nodeName(this,"body")||v(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(e,this.firstChild)})},before:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(e,this),"before",this.selector)}},after:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this.nextSibling)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(this,e),"after",this.selector)}},remove:function(e,t){var n,r=0;for(;(n=this[r])!=null;r++)if(!e||v.filter(e,[n]).length)!t&&n.nodeType===1&&(v.cleanData(n.getElementsByTagName("*")),v.cleanData([n])),n.parentNode&&n.parentNode.removeChild(n);return this},empty:function(){var e,t=0;for(;(e=this[t])!=null;t++){e.nodeType===1&&v.cleanData(e.getElementsByTagName("*"));while(e.firstChild)e.removeChild(e.firstChild)}return this},clone:function(e,t){return e=e==null?!1:e,t=t==null?e:t,this.map(function(){return v.clone(this,e,t)})},html:function(e){return v.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return n.nodeType===1?n.innerHTML.replace(ht,""):t;if(typeof e=="string"&&!yt.test(e)&&(v.support.htmlSerialize||!wt.test(e))&&(v.support.leadingWhitespace||!pt.test(e))&&!Nt[(vt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(dt,"<$1>");try{for(;r1&&typeof f=="string"&&St.test(f))return this.each(function(){v(this).domManip(e,n,r)});if(v.isFunction(f))return this.each(function(i){var s=v(this);e[0]=f.call(this,i,n?s.html():t),s.domManip(e,n,r)});if(this[0]){i=v.buildFragment(e,this,l),o=i.fragment,s=o.firstChild,o.childNodes.length===1&&(o=s);if(s){n=n&&v.nodeName(s,"tr");for(u=i.cacheable||c-1;a0?this.clone(!0):this).get(),v(o[i])[t](r),s=s.concat(r);return this.pushStack(s,e,o.selector)}}),v.extend({clone:function(e,t,n){var r,i,s,o;v.support.html5Clone||v.isXMLDoc(e)||!wt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(kt.innerHTML=e.outerHTML,kt.removeChild(o=kt.firstChild));if((!v.support.noCloneEvent||!v.support.noCloneChecked)&&(e.nodeType===1||e.nodeType===11)&&!v.isXMLDoc(e)){Ot(e,o),r=Mt(e),i=Mt(o);for(s=0;r[s];++s)i[s]&&Ot(r[s],i[s])}if(t){At(e,o);if(n){r=Mt(e),i=Mt(o);for(s=0;r[s];++s)At(r[s],i[s])}}return r=i=null,o},clean:function(e,t,n,r){var s,o,u,a,f,l,c,h,p,d,m,g,y=t===i&&Ct,b=[];if(!t||typeof t.createDocumentFragment=="undefined")t=i;for(s=0;(u=e[s])!=null;s++){typeof u=="number"&&(u+="");if(!u)continue;if(typeof u=="string")if(!gt.test(u))u=t.createTextNode(u);else{y=y||lt(t),c=t.createElement("div"),y.appendChild(c),u=u.replace(dt,"<$1>"),a=(vt.exec(u)||["",""])[1].toLowerCase(),f=Nt[a]||Nt._default,l=f[0],c.innerHTML=f[1]+u+f[2];while(l--)c=c.lastChild;if(!v.support.tbody){h=mt.test(u),p=a==="table"&&!h?c.firstChild&&c.firstChild.childNodes:f[1]===""&&!h?c.childNodes:[];for(o=p.length-1;o>=0;--o)v.nodeName(p[o],"tbody")&&!p[o].childNodes.length&&p[o].parentNode.removeChild(p[o])}!v.support.leadingWhitespace&&pt.test(u)&&c.insertBefore(t.createTextNode(pt.exec(u)[0]),c.firstChild),u=c.childNodes,c.parentNode.removeChild(c)}u.nodeType?b.push(u):v.merge(b,u)}c&&(u=c=y=null);if(!v.support.appendChecked)for(s=0;(u=b[s])!=null;s++)v.nodeName(u,"input")?_t(u):typeof u.getElementsByTagName!="undefined"&&v.grep(u.getElementsByTagName("input"),_t);if(n){m=function(e){if(!e.type||xt.test(e.type))return r?r.push(e.parentNode?e.parentNode.removeChild(e):e):n.appendChild(e)};for(s=0;(u=b[s])!=null;s++)if(!v.nodeName(u,"script")||!m(u))n.appendChild(u),typeof u.getElementsByTagName!="undefined"&&(g=v.grep(v.merge([],u.getElementsByTagName("script")),m),b.splice.apply(b,[s+1,0].concat(g)),s+=g.length)}return b},cleanData:function(e,t){var n,r,i,s,o=0,u=v.expando,a=v.cache,f=v.support.deleteExpando,l=v.event.special;for(;(i=e[o])!=null;o++)if(t||v.acceptData(i)){r=i[u],n=r&&a[r];if(n){if(n.events)for(s in n.events)l[s]?v.event.remove(i,s):v.removeEvent(i,s,n.handle);a[r]&&(delete a[r],f?delete i[u]:i.removeAttribute?i.removeAttribute(u):i[u]=null,v.deletedIds.push(r))}}}}),function(){var e,t;v.uaMatch=function(e){e=e.toLowerCase();var t=/(chrome)[ \/]([\w.]+)/.exec(e)||/(webkit)[ \/]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||e.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[];return{browser:t[1]||"",version:t[2]||"0"}},e=v.uaMatch(o.userAgent),t={},e.browser&&(t[e.browser]=!0,t.version=e.version),t.chrome?t.webkit=!0:t.webkit&&(t.safari=!0),v.browser=t,v.sub=function(){function e(t,n){return new e.fn.init(t,n)}v.extend(!0,e,this),e.superclass=this,e.fn=e.prototype=this(),e.fn.constructor=e,e.sub=this.sub,e.fn.init=function(r,i){return i&&i instanceof v&&!(i instanceof e)&&(i=e(i)),v.fn.init.call(this,r,i,t)},e.fn.init.prototype=e.fn;var t=e(i);return e}}();var Dt,Pt,Ht,Bt=/alpha\([^)]*\)/i,jt=/opacity=([^)]*)/,Ft=/^(top|right|bottom|left)$/,It=/^(none|table(?!-c[ea]).+)/,qt=/^margin/,Rt=new RegExp("^("+m+")(.*)$","i"),Ut=new RegExp("^("+m+")(?!px)[a-z%]+$","i"),zt=new RegExp("^([-+])=("+m+")","i"),Wt={BODY:"block"},Xt={position:"absolute",visibility:"hidden",display:"block"},Vt={letterSpacing:0,fontWeight:400},$t=["Top","Right","Bottom","Left"],Jt=["Webkit","O","Moz","ms"],Kt=v.fn.toggle;v.fn.extend({css:function(e,n){return v.access(this,function(e,n,r){return r!==t?v.style(e,n,r):v.css(e,n)},e,n,arguments.length>1)},show:function(){return Yt(this,!0)},hide:function(){return Yt(this)},toggle:function(e,t){var n=typeof e=="boolean";return v.isFunction(e)&&v.isFunction(t)?Kt.apply(this,arguments):this.each(function(){(n?e:Gt(this))?v(this).show():v(this).hide()})}}),v.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Dt(e,"opacity");return n===""?"1":n}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":v.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(!e||e.nodeType===3||e.nodeType===8||!e.style)return;var s,o,u,a=v.camelCase(n),f=e.style;n=v.cssProps[a]||(v.cssProps[a]=Qt(f,a)),u=v.cssHooks[n]||v.cssHooks[a];if(r===t)return u&&"get"in u&&(s=u.get(e,!1,i))!==t?s:f[n];o=typeof r,o==="string"&&(s=zt.exec(r))&&(r=(s[1]+1)*s[2]+parseFloat(v.css(e,n)),o="number");if(r==null||o==="number"&&isNaN(r))return;o==="number"&&!v.cssNumber[a]&&(r+="px");if(!u||!("set"in u)||(r=u.set(e,r,i))!==t)try{f[n]=r}catch(l){}},css:function(e,n,r,i){var s,o,u,a=v.camelCase(n);return n=v.cssProps[a]||(v.cssProps[a]=Qt(e.style,a)),u=v.cssHooks[n]||v.cssHooks[a],u&&"get"in u&&(s=u.get(e,!0,i)),s===t&&(s=Dt(e,n)),s==="normal"&&n in Vt&&(s=Vt[n]),r||i!==t?(o=parseFloat(s),r||v.isNumeric(o)?o||0:s):s},swap:function(e,t,n){var r,i,s={};for(i in t)s[i]=e.style[i],e.style[i]=t[i];r=n.call(e);for(i in t)e.style[i]=s[i];return r}}),e.getComputedStyle?Dt=function(t,n){var r,i,s,o,u=e.getComputedStyle(t,null),a=t.style;return u&&(r=u.getPropertyValue(n)||u[n],r===""&&!v.contains(t.ownerDocument,t)&&(r=v.style(t,n)),Ut.test(r)&&qt.test(n)&&(i=a.width,s=a.minWidth,o=a.maxWidth,a.minWidth=a.maxWidth=a.width=r,r=u.width,a.width=i,a.minWidth=s,a.maxWidth=o)),r}:i.documentElement.currentStyle&&(Dt=function(e,t){var n,r,i=e.currentStyle&&e.currentStyle[t],s=e.style;return i==null&&s&&s[t]&&(i=s[t]),Ut.test(i)&&!Ft.test(t)&&(n=s.left,r=e.runtimeStyle&&e.runtimeStyle.left,r&&(e.runtimeStyle.left=e.currentStyle.left),s.left=t==="fontSize"?"1em":i,i=s.pixelLeft+"px",s.left=n,r&&(e.runtimeStyle.left=r)),i===""?"auto":i}),v.each(["height","width"],function(e,t){v.cssHooks[t]={get:function(e,n,r){if(n)return e.offsetWidth===0&&It.test(Dt(e,"display"))?v.swap(e,Xt,function(){return tn(e,t,r)}):tn(e,t,r)},set:function(e,n,r){return Zt(e,n,r?en(e,t,r,v.support.boxSizing&&v.css(e,"boxSizing")==="border-box"):0)}}}),v.support.opacity||(v.cssHooks.opacity={get:function(e,t){return jt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=v.isNumeric(t)?"alpha(opacity="+t*100+")":"",s=r&&r.filter||n.filter||"";n.zoom=1;if(t>=1&&v.trim(s.replace(Bt,""))===""&&n.removeAttribute){n.removeAttribute("filter");if(r&&!r.filter)return}n.filter=Bt.test(s)?s.replace(Bt,i):s+" "+i}}),v(function(){v.support.reliableMarginRight||(v.cssHooks.marginRight={get:function(e,t){return v.swap(e,{display:"inline-block"},function(){if(t)return Dt(e,"marginRight")})}}),!v.support.pixelPosition&&v.fn.position&&v.each(["top","left"],function(e,t){v.cssHooks[t]={get:function(e,n){if(n){var r=Dt(e,t);return Ut.test(r)?v(e).position()[t]+"px":r}}}})}),v.expr&&v.expr.filters&&(v.expr.filters.hidden=function(e){return e.offsetWidth===0&&e.offsetHeight===0||!v.support.reliableHiddenOffsets&&(e.style&&e.style.display||Dt(e,"display"))==="none"},v.expr.filters.visible=function(e){return!v.expr.filters.hidden(e)}),v.each({margin:"",padding:"",border:"Width"},function(e,t){v.cssHooks[e+t]={expand:function(n){var r,i=typeof n=="string"?n.split(" "):[n],s={};for(r=0;r<4;r++)s[e+$t[r]+t]=i[r]||i[r-2]||i[0];return s}},qt.test(e)||(v.cssHooks[e+t].set=Zt)});var rn=/%20/g,sn=/\[\]$/,on=/\r?\n/g,un=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,an=/^(?:select|textarea)/i;v.fn.extend({serialize:function(){return v.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?v.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||an.test(this.nodeName)||un.test(this.type))}).map(function(e,t){var n=v(this).val();return n==null?null:v.isArray(n)?v.map(n,function(e,n){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),v.param=function(e,n){var r,i=[],s=function(e,t){t=v.isFunction(t)?t():t==null?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};n===t&&(n=v.ajaxSettings&&v.ajaxSettings.traditional);if(v.isArray(e)||e.jquery&&!v.isPlainObject(e))v.each(e,function(){s(this.name,this.value)});else for(r in e)fn(r,e[r],n,s);return i.join("&").replace(rn,"+")};var ln,cn,hn=/#.*$/,pn=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,dn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,vn=/^(?:GET|HEAD)$/,mn=/^\/\//,gn=/\?/,yn=/)<[^<]*)*<\/script>/gi,bn=/([?&])_=[^&]*/,wn=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,En=v.fn.load,Sn={},xn={},Tn=["*/"]+["*"];try{cn=s.href}catch(Nn){cn=i.createElement("a"),cn.href="",cn=cn.href}ln=wn.exec(cn.toLowerCase())||[],v.fn.load=function(e,n,r){if(typeof e!="string"&&En)return En.apply(this,arguments);if(!this.length)return this;var i,s,o,u=this,a=e.indexOf(" ");return a>=0&&(i=e.slice(a,e.length),e=e.slice(0,a)),v.isFunction(n)?(r=n,n=t):n&&typeof n=="object"&&(s="POST"),v.ajax({url:e,type:s,dataType:"html",data:n,complete:function(e,t){r&&u.each(r,o||[e.responseText,t,e])}}).done(function(e){o=arguments,u.html(i?v("
    ").append(e.replace(yn,"")).find(i):e)}),this},v.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(e,t){v.fn[t]=function(e){return this.on(t,e)}}),v.each(["get","post"],function(e,n){v[n]=function(e,r,i,s){return v.isFunction(r)&&(s=s||i,i=r,r=t),v.ajax({type:n,url:e,data:r,success:i,dataType:s})}}),v.extend({getScript:function(e,n){return v.get(e,t,n,"script")},getJSON:function(e,t,n){return v.get(e,t,n,"json")},ajaxSetup:function(e,t){return t?Ln(e,v.ajaxSettings):(t=e,e=v.ajaxSettings),Ln(e,t),e},ajaxSettings:{url:cn,isLocal:dn.test(ln[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":Tn},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":v.parseJSON,"text xml":v.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:Cn(Sn),ajaxTransport:Cn(xn),ajax:function(e,n){function T(e,n,s,a){var l,y,b,w,S,T=n;if(E===2)return;E=2,u&&clearTimeout(u),o=t,i=a||"",x.readyState=e>0?4:0,s&&(w=An(c,x,s));if(e>=200&&e<300||e===304)c.ifModified&&(S=x.getResponseHeader("Last-Modified"),S&&(v.lastModified[r]=S),S=x.getResponseHeader("Etag"),S&&(v.etag[r]=S)),e===304?(T="notmodified",l=!0):(l=On(c,w),T=l.state,y=l.data,b=l.error,l=!b);else{b=T;if(!T||e)T="error",e<0&&(e=0)}x.status=e,x.statusText=(n||T)+"",l?d.resolveWith(h,[y,T,x]):d.rejectWith(h,[x,T,b]),x.statusCode(g),g=t,f&&p.trigger("ajax"+(l?"Success":"Error"),[x,c,l?y:b]),m.fireWith(h,[x,T]),f&&(p.trigger("ajaxComplete",[x,c]),--v.active||v.event.trigger("ajaxStop"))}typeof e=="object"&&(n=e,e=t),n=n||{};var r,i,s,o,u,a,f,l,c=v.ajaxSetup({},n),h=c.context||c,p=h!==c&&(h.nodeType||h instanceof v)?v(h):v.event,d=v.Deferred(),m=v.Callbacks("once memory"),g=c.statusCode||{},b={},w={},E=0,S="canceled",x={readyState:0,setRequestHeader:function(e,t){if(!E){var n=e.toLowerCase();e=w[n]=w[n]||e,b[e]=t}return this},getAllResponseHeaders:function(){return E===2?i:null},getResponseHeader:function(e){var n;if(E===2){if(!s){s={};while(n=pn.exec(i))s[n[1].toLowerCase()]=n[2]}n=s[e.toLowerCase()]}return n===t?null:n},overrideMimeType:function(e){return E||(c.mimeType=e),this},abort:function(e){return e=e||S,o&&o.abort(e),T(0,e),this}};d.promise(x),x.success=x.done,x.error=x.fail,x.complete=m.add,x.statusCode=function(e){if(e){var t;if(E<2)for(t in e)g[t]=[g[t],e[t]];else t=e[x.status],x.always(t)}return this},c.url=((e||c.url)+"").replace(hn,"").replace(mn,ln[1]+"//"),c.dataTypes=v.trim(c.dataType||"*").toLowerCase().split(y),c.crossDomain==null&&(a=wn.exec(c.url.toLowerCase()),c.crossDomain=!(!a||a[1]===ln[1]&&a[2]===ln[2]&&(a[3]||(a[1]==="http:"?80:443))==(ln[3]||(ln[1]==="http:"?80:443)))),c.data&&c.processData&&typeof c.data!="string"&&(c.data=v.param(c.data,c.traditional)),kn(Sn,c,n,x);if(E===2)return x;f=c.global,c.type=c.type.toUpperCase(),c.hasContent=!vn.test(c.type),f&&v.active++===0&&v.event.trigger("ajaxStart");if(!c.hasContent){c.data&&(c.url+=(gn.test(c.url)?"&":"?")+c.data,delete c.data),r=c.url;if(c.cache===!1){var N=v.now(),C=c.url.replace(bn,"$1_="+N);c.url=C+(C===c.url?(gn.test(c.url)?"&":"?")+"_="+N:"")}}(c.data&&c.hasContent&&c.contentType!==!1||n.contentType)&&x.setRequestHeader("Content-Type",c.contentType),c.ifModified&&(r=r||c.url,v.lastModified[r]&&x.setRequestHeader("If-Modified-Since",v.lastModified[r]),v.etag[r]&&x.setRequestHeader("If-None-Match",v.etag[r])),x.setRequestHeader("Accept",c.dataTypes[0]&&c.accepts[c.dataTypes[0]]?c.accepts[c.dataTypes[0]]+(c.dataTypes[0]!=="*"?", "+Tn+"; q=0.01":""):c.accepts["*"]);for(l in c.headers)x.setRequestHeader(l,c.headers[l]);if(!c.beforeSend||c.beforeSend.call(h,x,c)!==!1&&E!==2){S="abort";for(l in{success:1,error:1,complete:1})x[l](c[l]);o=kn(xn,c,n,x);if(!o)T(-1,"No Transport");else{x.readyState=1,f&&p.trigger("ajaxSend",[x,c]),c.async&&c.timeout>0&&(u=setTimeout(function(){x.abort("timeout")},c.timeout));try{E=1,o.send(b,T)}catch(k){if(!(E<2))throw k;T(-1,k)}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var Mn=[],_n=/\?/,Dn=/(=)\?(?=&|$)|\?\?/,Pn=v.now();v.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Mn.pop()||v.expando+"_"+Pn++;return this[e]=!0,e}}),v.ajaxPrefilter("json jsonp",function(n,r,i){var s,o,u,a=n.data,f=n.url,l=n.jsonp!==!1,c=l&&Dn.test(f),h=l&&!c&&typeof a=="string"&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Dn.test(a);if(n.dataTypes[0]==="jsonp"||c||h)return s=n.jsonpCallback=v.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,o=e[s],c?n.url=f.replace(Dn,"$1"+s):h?n.data=a.replace(Dn,"$1"+s):l&&(n.url+=(_n.test(f)?"&":"?")+n.jsonp+"="+s),n.converters["script json"]=function(){return u||v.error(s+" was not called"),u[0]},n.dataTypes[0]="json",e[s]=function(){u=arguments},i.always(function(){e[s]=o,n[s]&&(n.jsonpCallback=r.jsonpCallback,Mn.push(s)),u&&v.isFunction(o)&&o(u[0]),u=o=t}),"script"}),v.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(e){return v.globalEval(e),e}}}),v.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),v.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=i.head||i.getElementsByTagName("head")[0]||i.documentElement;return{send:function(s,o){n=i.createElement("script"),n.async="async",e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,i){if(i||!n.readyState||/loaded|complete/.test(n.readyState))n.onload=n.onreadystatechange=null,r&&n.parentNode&&r.removeChild(n),n=t,i||o(200,"success")},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(0,1)}}}});var Hn,Bn=e.ActiveXObject?function(){for(var e in Hn)Hn[e](0,1)}:!1,jn=0;v.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&Fn()||In()}:Fn,function(e){v.extend(v.support,{ajax:!!e,cors:!!e&&"withCredentials"in e})}(v.ajaxSettings.xhr()),v.support.ajax&&v.ajaxTransport(function(n){if(!n.crossDomain||v.support.cors){var r;return{send:function(i,s){var o,u,a=n.xhr();n.username?a.open(n.type,n.url,n.async,n.username,n.password):a.open(n.type,n.url,n.async);if(n.xhrFields)for(u in n.xhrFields)a[u]=n.xhrFields[u];n.mimeType&&a.overrideMimeType&&a.overrideMimeType(n.mimeType),!n.crossDomain&&!i["X-Requested-With"]&&(i["X-Requested-With"]="XMLHttpRequest");try{for(u in i)a.setRequestHeader(u,i[u])}catch(f){}a.send(n.hasContent&&n.data||null),r=function(e,i){var u,f,l,c,h;try{if(r&&(i||a.readyState===4)){r=t,o&&(a.onreadystatechange=v.noop,Bn&&delete Hn[o]);if(i)a.readyState!==4&&a.abort();else{u=a.status,l=a.getAllResponseHeaders(),c={},h=a.responseXML,h&&h.documentElement&&(c.xml=h);try{c.text=a.responseText}catch(p){}try{f=a.statusText}catch(p){f=""}!u&&n.isLocal&&!n.crossDomain?u=c.text?200:404:u===1223&&(u=204)}}}catch(d){i||s(-1,d)}c&&s(u,f,c,l)},n.async?a.readyState===4?setTimeout(r,0):(o=++jn,Bn&&(Hn||(Hn={},v(e).unload(Bn)),Hn[o]=r),a.onreadystatechange=r):r()},abort:function(){r&&r(0,1)}}}});var qn,Rn,Un=/^(?:toggle|show|hide)$/,zn=new RegExp("^(?:([-+])=|)("+m+")([a-z%]*)$","i"),Wn=/queueHooks$/,Xn=[Gn],Vn={"*":[function(e,t){var n,r,i=this.createTween(e,t),s=zn.exec(t),o=i.cur(),u=+o||0,a=1,f=20;if(s){n=+s[2],r=s[3]||(v.cssNumber[e]?"":"px");if(r!=="px"&&u){u=v.css(i.elem,e,!0)||n||1;do a=a||".5",u/=a,v.style(i.elem,e,u+r);while(a!==(a=i.cur()/o)&&a!==1&&--f)}i.unit=r,i.start=u,i.end=s[1]?u+(s[1]+1)*n:n}return i}]};v.Animation=v.extend(Kn,{tweener:function(e,t){v.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;r-1,f={},l={},c,h;a?(l=i.position(),c=l.top,h=l.left):(c=parseFloat(o)||0,h=parseFloat(u)||0),v.isFunction(t)&&(t=t.call(e,n,s)),t.top!=null&&(f.top=t.top-s.top+c),t.left!=null&&(f.left=t.left-s.left+h),"using"in t?t.using.call(e,f):i.css(f)}},v.fn.extend({position:function(){if(!this[0])return;var e=this[0],t=this.offsetParent(),n=this.offset(),r=er.test(t[0].nodeName)?{top:0,left:0}:t.offset();return n.top-=parseFloat(v.css(e,"marginTop"))||0,n.left-=parseFloat(v.css(e,"marginLeft"))||0,r.top+=parseFloat(v.css(t[0],"borderTopWidth"))||0,r.left+=parseFloat(v.css(t[0],"borderLeftWidth"))||0,{top:n.top-r.top,left:n.left-r.left}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||i.body;while(e&&!er.test(e.nodeName)&&v.css(e,"position")==="static")e=e.offsetParent;return e||i.body})}}),v.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);v.fn[e]=function(i){return v.access(this,function(e,i,s){var o=tr(e);if(s===t)return o?n in o?o[n]:o.document.documentElement[i]:e[i];o?o.scrollTo(r?v(o).scrollLeft():s,r?s:v(o).scrollTop()):e[i]=s},e,i,arguments.length,null)}}),v.each({Height:"height",Width:"width"},function(e,n){v.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){v.fn[i]=function(i,s){var o=arguments.length&&(r||typeof i!="boolean"),u=r||(i===!0||s===!0?"margin":"border");return v.access(this,function(n,r,i){var s;return v.isWindow(n)?n.document.documentElement["client"+e]:n.nodeType===9?(s=n.documentElement,Math.max(n.body["scroll"+e],s["scroll"+e],n.body["offset"+e],s["offset"+e],s["client"+e])):i===t?v.css(n,r,i,u):v.style(n,r,i,u)},n,o?i:t,o,null)}})}),e.jQuery=e.$=v,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return v})})(window); -; - -/*! ========================================================= +"object"!=typeof JSON&&(JSON={}),function(){"use strict";function f(e){return 10>e?"0"+e:e}function quote(e){return escapable.lastIndex=0,escapable.test(e)?'"'+e.replace(escapable,function(e){var t=meta[e];return"string"==typeof t?t:"\\u"+("0000"+e.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+e+'"'}function str(e,t){var n,r,o,i,a,s=gap,l=t[e];switch(l&&"object"==typeof l&&"function"==typeof l.toJSON&&(l=l.toJSON(e)),"function"==typeof rep&&(l=rep.call(t,e,l)),typeof l){case"string":return quote(l);case"number":return isFinite(l)?l+"":"null";case"boolean":case"null":return l+"";case"object":if(!l)return"null";if(gap+=indent,a=[],"[object Array]"===Object.prototype.toString.apply(l)){for(i=l.length,n=0;i>n;n+=1)a[n]=str(n,l)||"null";return o=0===a.length?"[]":gap?"[\n"+gap+a.join(",\n"+gap)+"\n"+s+"]":"["+a.join(",")+"]",gap=s,o}if(rep&&"object"==typeof rep)for(i=rep.length,n=0;i>n;n+=1)"string"==typeof rep[n]&&(r=rep[n],o=str(r,l),o&&a.push(quote(r)+(gap?": ":":")+o));else for(r in l)Object.prototype.hasOwnProperty.call(l,r)&&(o=str(r,l),o&&a.push(quote(r)+(gap?": ":":")+o));return o=0===a.length?"{}":gap?"{\n"+gap+a.join(",\n"+gap)+"\n"+s+"}":"{"+a.join(",")+"}",gap=s,o}}"function"!=typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()});var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b"," ":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;"function"!=typeof JSON.stringify&&(JSON.stringify=function(e,t,n){var r;if(gap="",indent="","number"==typeof n)for(r=0;n>r;r+=1)indent+=" ";else"string"==typeof n&&(indent=n);if(rep=t,t&&"function"!=typeof t&&("object"!=typeof t||"number"!=typeof t.length))throw Error("JSON.stringify");return str("",{"":e})}),"function"!=typeof JSON.parse&&(JSON.parse=function(text,reviver){function walk(e,t){var n,r,o=e[t];if(o&&"object"==typeof o)for(n in o)Object.prototype.hasOwnProperty.call(o,n)&&(r=walk(o,n),void 0!==r?o[n]=r:delete o[n]);return reviver.call(e,t,o)}var j;if(text+="",cx.lastIndex=0,cx.test(text)&&(text=text.replace(cx,function(e){return"\\u"+("0000"+e.charCodeAt(0).toString(16)).slice(-4)})),/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return j=eval("("+text+")"),"function"==typeof reviver?walk({"":j},""):j;throw new SyntaxError("JSON.parse")})}(),/*! + * jQuery JavaScript Library v1.8.3 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2012 jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: Tue Nov 13 2012 08:20:33 GMT-0500 (Eastern Standard Time) + */ +function(e,t){function n(e){var t=ht[e]={};return Y.each(e.split(tt),function(e,n){t[n]=!0}),t}function r(e,n,r){if(r===t&&1===e.nodeType){var o="data-"+n.replace(mt,"-$1").toLowerCase();if(r=e.getAttribute(o),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:gt.test(r)?Y.parseJSON(r):r}catch(i){}Y.data(e,n,r)}else r=t}return r}function o(e){var t;for(t in e)if(("data"!==t||!Y.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function i(){return!1}function a(){return!0}function s(e){return!e||!e.parentNode||11===e.parentNode.nodeType}function l(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function c(e,t,n){if(t=t||0,Y.isFunction(t))return Y.grep(e,function(e,r){var o=!!t.call(e,r,e);return o===n});if(t.nodeType)return Y.grep(e,function(e){return e===t===n});if("string"==typeof t){var r=Y.grep(e,function(e){return 1===e.nodeType});if(It.test(t))return Y.filter(t,r,!n);t=Y.filter(t,r)}return Y.grep(e,function(e){return Y.inArray(e,t)>=0===n})}function u(e){var t=Lt.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function p(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function f(e,t){if(1===t.nodeType&&Y.hasData(e)){var n,r,o,i=Y._data(e),a=Y._data(t,i),s=i.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,o=s[n].length;o>r;r++)Y.event.add(t,n,s[n][r])}a.data&&(a.data=Y.extend({},a.data))}}function d(e,t){var n;1===t.nodeType&&(t.clearAttributes&&t.clearAttributes(),t.mergeAttributes&&t.mergeAttributes(e),n=t.nodeName.toLowerCase(),"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),Y.support.html5Clone&&e.innerHTML&&!Y.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Vt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.selected=e.defaultSelected:"input"===n||"textarea"===n?t.defaultValue=e.defaultValue:"script"===n&&t.text!==e.text&&(t.text=e.text),t.removeAttribute(Y.expando))}function h(e){return e.getElementsByTagName!==t?e.getElementsByTagName("*"):e.querySelectorAll!==t?e.querySelectorAll("*"):[]}function g(e){Vt.test(e.type)&&(e.defaultChecked=e.checked)}function m(e,t){if(t in e)return t;for(var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,o=vn.length;o--;)if(t=vn[o]+n,t in e)return t;return r}function y(e,t){return e=t||e,"none"===Y.css(e,"display")||!Y.contains(e.ownerDocument,e)}function v(e,t){for(var n,r,o=[],i=0,a=e.length;a>i;i++)n=e[i],n.style&&(o[i]=Y._data(n,"olddisplay"),t?(o[i]||"none"!==n.style.display||(n.style.display=""),""===n.style.display&&y(n)&&(o[i]=Y._data(n,"olddisplay",_(n.nodeName)))):(r=nn(n,"display"),o[i]||"none"===r||Y._data(n,"olddisplay",r)));for(i=0;a>i;i++)n=e[i],n.style&&(t&&"none"!==n.style.display&&""!==n.style.display||(n.style.display=t?o[i]||"":"none"));return e}function b(e,t,n){var r=pn.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function x(e,t,n,r){for(var o=n===(r?"border":"content")?4:"width"===t?1:0,i=0;4>o;o+=2)"margin"===n&&(i+=Y.css(e,n+yn[o],!0)),r?("content"===n&&(i-=parseFloat(nn(e,"padding"+yn[o]))||0),"margin"!==n&&(i-=parseFloat(nn(e,"border"+yn[o]+"Width"))||0)):(i+=parseFloat(nn(e,"padding"+yn[o]))||0,"padding"!==n&&(i+=parseFloat(nn(e,"border"+yn[o]+"Width"))||0));return i}function w(e,t,n){var r="width"===t?e.offsetWidth:e.offsetHeight,o=!0,i=Y.support.boxSizing&&"border-box"===Y.css(e,"boxSizing");if(0>=r||null==r){if(r=nn(e,t),(0>r||null==r)&&(r=e.style[t]),fn.test(r))return r;o=i&&(Y.support.boxSizingReliable||r===e.style[t]),r=parseFloat(r)||0}return r+x(e,t,n||(i?"border":"content"),o)+"px"}function _(e){if(hn[e])return hn[e];var t=Y("<"+e+">").appendTo($.body),n=t.css("display");return t.remove(),("none"===n||""===n)&&(rn=$.body.appendChild(rn||Y.extend($.createElement("iframe"),{frameBorder:0,width:0,height:0})),on&&rn.createElement||(on=(rn.contentWindow||rn.contentDocument).document,on.write(""),on.close()),t=on.body.appendChild(on.createElement(e)),n=nn(t,"display"),$.body.removeChild(rn)),hn[e]=n,n}function C(e,t,n,r){var o;if(Y.isArray(t))Y.each(t,function(t,o){n||wn.test(e)?r(e,o):C(e+"["+("object"==typeof o?t:"")+"]",o,n,r)});else if(n||"object"!==Y.type(t))r(e,t);else for(o in t)C(e+"["+o+"]",t[o],n,r)}function k(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,o,i,a=t.toLowerCase().split(tt),s=0,l=a.length;if(Y.isFunction(n))for(;l>s;s++)r=a[s],i=/^\+/.test(r),i&&(r=r.substr(1)||"*"),o=e[r]=e[r]||[],o[i?"unshift":"push"](n)}}function A(e,n,r,o,i,a){i=i||n.dataTypes[0],a=a||{},a[i]=!0;for(var s,l=e[i],c=0,u=l?l.length:0,p=e===Pn;u>c&&(p||!s);c++)s=l[c](n,r,o),"string"==typeof s&&(!p||a[s]?s=t:(n.dataTypes.unshift(s),s=A(e,n,r,o,s,a)));return!p&&s||a["*"]||(s=A(e,n,r,o,"*",a)),s}function T(e,n){var r,o,i=Y.ajaxSettings.flatOptions||{};for(r in n)n[r]!==t&&((i[r]?e:o||(o={}))[r]=n[r]);o&&Y.extend(!0,e,o)}function E(e,n,r){var o,i,a,s,l=e.contents,c=e.dataTypes,u=e.responseFields;for(i in u)i in r&&(n[u[i]]=r[i]);for(;"*"===c[0];)c.shift(),o===t&&(o=e.mimeType||n.getResponseHeader("content-type"));if(o)for(i in l)if(l[i]&&l[i].test(o)){c.unshift(i);break}if(c[0]in r)a=c[0];else{for(i in r){if(!c[0]||e.converters[i+" "+c[0]]){a=i;break}s||(s=i)}a=a||s}return a?(a!==c[0]&&c.unshift(a),r[a]):t}function F(e,t){var n,r,o,i,a=e.dataTypes.slice(),s=a[0],l={},c=0;if(e.dataFilter&&(t=e.dataFilter(t,e.dataType)),a[1])for(n in e.converters)l[n.toLowerCase()]=e.converters[n];for(;o=a[++c];)if("*"!==o){if("*"!==s&&s!==o){if(n=l[s+" "+o]||l["* "+o],!n)for(r in l)if(i=r.split(" "),i[1]===o&&(n=l[s+" "+i[0]]||l["* "+i[0]])){n===!0?n=l[r]:l[r]!==!0&&(o=i[0],a.splice(c--,0,o));break}if(n!==!0)if(n&&e["throws"])t=n(t);else try{t=n(t)}catch(u){return{state:"parsererror",error:n?u:"No conversion from "+s+" to "+o}}}s=o}return{state:"success",data:t}}function N(){try{return new e.XMLHttpRequest}catch(t){}}function S(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}function j(){return setTimeout(function(){Jn=t},0),Jn=Y.now()}function R(e,t){Y.each(t,function(t,n){for(var r=(er[t]||[]).concat(er["*"]),o=0,i=r.length;i>o;o++)if(r[o].call(e,t,n))return})}function O(e,t,n){var r,o=0,i=Zn.length,a=Y.Deferred().always(function(){delete s.elem}),s=function(){for(var t=Jn||j(),n=Math.max(0,l.startTime+l.duration-t),r=n/l.duration||0,o=1-r,i=0,s=l.tweens.length;s>i;i++)l.tweens[i].run(o);return a.notifyWith(e,[l,o,n]),1>o&&s?n:(a.resolveWith(e,[l]),!1)},l=a.promise({elem:e,props:Y.extend({},t),opts:Y.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Jn||j(),duration:n.duration,tweens:[],createTween:function(t,n){var r=Y.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){for(var n=0,r=t?l.tweens.length:0;r>n;n++)l.tweens[n].run(1);return t?a.resolveWith(e,[l,t]):a.rejectWith(e,[l,t]),this}}),c=l.props;for(M(c,l.opts.specialEasing);i>o;o++)if(r=Zn[o].call(l,e,c,l.opts))return r;return R(l,c),Y.isFunction(l.opts.start)&&l.opts.start.call(e,l),Y.fx.timer(Y.extend(s,{anim:l,queue:l.opts.queue,elem:e})),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always)}function M(e,t){var n,r,o,i,a;for(n in e)if(r=Y.camelCase(n),o=t[r],i=e[n],Y.isArray(i)&&(o=i[1],i=e[n]=i[0]),n!==r&&(e[r]=i,delete e[n]),a=Y.cssHooks[r],a&&"expand"in a){i=a.expand(i),delete e[r];for(n in i)n in e||(e[n]=i[n],t[n]=o)}else t[r]=o}function H(e,t,n){var r,o,i,a,s,l,c,u,p,f=this,d=e.style,h={},g=[],m=e.nodeType&&y(e);n.queue||(u=Y._queueHooks(e,"fx"),null==u.unqueued&&(u.unqueued=0,p=u.empty.fire,u.empty.fire=function(){u.unqueued||p()}),u.unqueued++,f.always(function(){f.always(function(){u.unqueued--,Y.queue(e,"fx").length||u.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[d.overflow,d.overflowX,d.overflowY],"inline"===Y.css(e,"display")&&"none"===Y.css(e,"float")&&(Y.support.inlineBlockNeedsLayout&&"inline"!==_(e.nodeName)?d.zoom=1:d.display="inline-block")),n.overflow&&(d.overflow="hidden",Y.support.shrinkWrapBlocks||f.done(function(){d.overflow=n.overflow[0],d.overflowX=n.overflow[1],d.overflowY=n.overflow[2]}));for(r in t)if(i=t[r],Kn.exec(i)){if(delete t[r],l=l||"toggle"===i,i===(m?"hide":"show"))continue;g.push(r)}if(a=g.length){s=Y._data(e,"fxshow")||Y._data(e,"fxshow",{}),"hidden"in s&&(m=s.hidden),l&&(s.hidden=!m),m?Y(e).show():f.done(function(){Y(e).hide()}),f.done(function(){var t;Y.removeData(e,"fxshow",!0);for(t in h)Y.style(e,t,h[t])});for(r=0;a>r;r++)o=g[r],c=f.createTween(o,m?s[o]:0),h[o]=s[o]||Y.style(e,o),o in s||(s[o]=c.start,m&&(c.end=c.start,c.start="width"===o||"height"===o?1:0))}}function I(e,t,n,r,o){return new I.prototype.init(e,t,n,r,o)}function P(e,t){var n,r={height:e},o=0;for(t=t?1:0;4>o;o+=2-t)n=yn[o],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}function D(e){return Y.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}var L,B,$=e.document,q=e.location,W=e.navigator,U=e.jQuery,Q=e.$,z=Array.prototype.push,X=Array.prototype.slice,J=Array.prototype.indexOf,V=Object.prototype.toString,K=Object.prototype.hasOwnProperty,G=String.prototype.trim,Y=function(e,t){return new Y.fn.init(e,t,L)},Z=/[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source,et=/\S/,tt=/\s+/,nt=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,rt=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,ot=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,it=/^[\],:{}\s]*$/,at=/(?:^|:|,)(?:\s*\[)+/g,st=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,lt=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,ct=/^-ms-/,ut=/-([\da-z])/gi,pt=function(e,t){return(t+"").toUpperCase()},ft=function(){$.addEventListener?($.removeEventListener("DOMContentLoaded",ft,!1),Y.ready()):"complete"===$.readyState&&($.detachEvent("onreadystatechange",ft),Y.ready())},dt={};Y.fn=Y.prototype={constructor:Y,init:function(e,n,r){var o,i,a;if(!e)return this;if(e.nodeType)return this.context=this[0]=e,this.length=1,this;if("string"==typeof e){if(o="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:rt.exec(e),!o||!o[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(o[1])return n=n instanceof Y?n[0]:n,a=n&&n.nodeType?n.ownerDocument||n:$,e=Y.parseHTML(o[1],a,!0),ot.test(o[1])&&Y.isPlainObject(n)&&this.attr.call(e,n,!0),Y.merge(this,e);if(i=$.getElementById(o[2]),i&&i.parentNode){if(i.id!==o[2])return r.find(e);this.length=1,this[0]=i}return this.context=$,this.selector=e,this}return Y.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),Y.makeArray(e,this))},selector:"",jquery:"1.8.3",length:0,size:function(){return this.length},toArray:function(){return X.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e,t,n){var r=Y.merge(this.constructor(),e);return r.prevObject=this,r.context=this.context,"find"===t?r.selector=this.selector+(this.selector?" ":"")+n:t&&(r.selector=this.selector+"."+t+"("+n+")"),r},each:function(e,t){return Y.each(this,e,t)},ready:function(e){return Y.ready.promise().done(e),this},eq:function(e){return e=+e,-1===e?this.slice(e):this.slice(e,e+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(X.apply(this,arguments),"slice",X.call(arguments).join(","))},map:function(e){return this.pushStack(Y.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:z,sort:[].sort,splice:[].splice},Y.fn.init.prototype=Y.fn,Y.extend=Y.fn.extend=function(){var e,n,r,o,i,a,s=arguments[0]||{},l=1,c=arguments.length,u=!1;for("boolean"==typeof s&&(u=s,s=arguments[1]||{},l=2),"object"==typeof s||Y.isFunction(s)||(s={}),c===l&&(s=this,--l);c>l;l++)if(null!=(e=arguments[l]))for(n in e)r=s[n],o=e[n],s!==o&&(u&&o&&(Y.isPlainObject(o)||(i=Y.isArray(o)))?(i?(i=!1,a=r&&Y.isArray(r)?r:[]):a=r&&Y.isPlainObject(r)?r:{},s[n]=Y.extend(u,a,o)):o!==t&&(s[n]=o));return s},Y.extend({noConflict:function(t){return e.$===Y&&(e.$=Q),t&&e.jQuery===Y&&(e.jQuery=U),Y},isReady:!1,readyWait:1,holdReady:function(e){e?Y.readyWait++:Y.ready(!0)},ready:function(e){if(e===!0?!--Y.readyWait:!Y.isReady){if(!$.body)return setTimeout(Y.ready,1);Y.isReady=!0,e!==!0&&--Y.readyWait>0||(B.resolveWith($,[Y]),Y.fn.trigger&&Y($).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===Y.type(e)},isArray:Array.isArray||function(e){return"array"===Y.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":dt[V.call(e)]||"object"},isPlainObject:function(e){if(!e||"object"!==Y.type(e)||e.nodeType||Y.isWindow(e))return!1;try{if(e.constructor&&!K.call(e,"constructor")&&!K.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||K.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){var r;return e&&"string"==typeof e?("boolean"==typeof t&&(n=t,t=0),t=t||$,(r=ot.exec(e))?[t.createElement(r[1])]:(r=Y.buildFragment([e],t,n?null:[]),Y.merge([],(r.cacheable?Y.clone(r.fragment):r.fragment).childNodes))):null},parseJSON:function(n){return n&&"string"==typeof n?(n=Y.trim(n),e.JSON&&e.JSON.parse?e.JSON.parse(n):it.test(n.replace(st,"@").replace(lt,"]").replace(at,""))?Function("return "+n)():(Y.error("Invalid JSON: "+n),t)):null},parseXML:function(n){var r,o;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(o=new DOMParser,r=o.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(i){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||Y.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&et.test(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(ct,"ms-").replace(ut,pt)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,n,r){var o,i=0,a=e.length,s=a===t||Y.isFunction(e);if(r)if(s){for(o in e)if(n.apply(e[o],r)===!1)break}else for(;a>i&&n.apply(e[i++],r)!==!1;);else if(s){for(o in e)if(n.call(e[o],o,e[o])===!1)break}else for(;a>i&&n.call(e[i],i,e[i++])!==!1;);return e},trim:G&&!G.call(" ")?function(e){return null==e?"":G.call(e)}:function(e){return null==e?"":(e+"").replace(nt,"")},makeArray:function(e,t){var n,r=t||[];return null!=e&&(n=Y.type(e),null==e.length||"string"===n||"function"===n||"regexp"===n||Y.isWindow(e)?z.call(r,e):Y.merge(r,e)),r},inArray:function(e,t,n){var r;if(t){if(J)return J.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,o=e.length,i=0;if("number"==typeof r)for(;r>i;i++)e[o++]=n[i];else for(;n[i]!==t;)e[o++]=n[i++];return e.length=o,e},grep:function(e,t,n){var r,o=[],i=0,a=e.length;for(n=!!n;a>i;i++)r=!!t(e[i],i),n!==r&&o.push(e[i]);return o},map:function(e,n,r){var o,i,a=[],s=0,l=e.length,c=e instanceof Y||l!==t&&"number"==typeof l&&(l>0&&e[0]&&e[l-1]||0===l||Y.isArray(e));if(c)for(;l>s;s++)o=n(e[s],s,r),null!=o&&(a[a.length]=o);else for(i in e)o=n(e[i],i,r),null!=o&&(a[a.length]=o);return a.concat.apply([],a)},guid:1,proxy:function(e,n){var r,o,i;return"string"==typeof n&&(r=e[n],n=e,e=r),Y.isFunction(e)?(o=X.call(arguments,2),i=function(){return e.apply(n,o.concat(X.call(arguments)))},i.guid=e.guid=e.guid||Y.guid++,i):t},access:function(e,n,r,o,i,a,s){var l,c=null==r,u=0,p=e.length;if(r&&"object"==typeof r){for(u in r)Y.access(e,n,u,r[u],1,a,o);i=1}else if(o!==t){if(l=s===t&&Y.isFunction(o),c&&(l?(l=n,n=function(e,t,n){return l.call(Y(e),n)}):(n.call(e,o),n=null)),n)for(;p>u;u++)n(e[u],r,l?o.call(e[u],u,n(e[u],r)):o,s);i=1}return i?e:c?n.call(e):p?n(e[0],r):a},now:function(){return(new Date).getTime()}}),Y.ready.promise=function(t){if(!B)if(B=Y.Deferred(),"complete"===$.readyState)setTimeout(Y.ready,1);else if($.addEventListener)$.addEventListener("DOMContentLoaded",ft,!1),e.addEventListener("load",Y.ready,!1);else{$.attachEvent("onreadystatechange",ft),e.attachEvent("onload",Y.ready);var n=!1;try{n=null==e.frameElement&&$.documentElement}catch(r){}n&&n.doScroll&&function o(){if(!Y.isReady){try{n.doScroll("left")}catch(e){return setTimeout(o,50)}Y.ready()}}()}return B.promise(t)},Y.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(e,t){dt["[object "+t+"]"]=t.toLowerCase()}),L=Y($);var ht={};Y.Callbacks=function(e){e="string"==typeof e?ht[e]||n(e):Y.extend({},e);var r,o,i,a,s,l,c=[],u=!e.once&&[],p=function(t){for(r=e.memory&&t,o=!0,l=a||0,a=0,s=c.length,i=!0;c&&s>l;l++)if(c[l].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}i=!1,c&&(u?u.length&&p(u.shift()):r?c=[]:f.disable())},f={add:function(){if(c){var t=c.length;(function n(t){Y.each(t,function(t,r){var o=Y.type(r);"function"===o?e.unique&&f.has(r)||c.push(r):r&&r.length&&"string"!==o&&n(r)})})(arguments),i?s=c.length:r&&(a=t,p(r))}return this},remove:function(){return c&&Y.each(arguments,function(e,t){for(var n;(n=Y.inArray(t,c,n))>-1;)c.splice(n,1),i&&(s>=n&&s--,l>=n&&l--)}),this},has:function(e){return Y.inArray(e,c)>-1},empty:function(){return c=[],this},disable:function(){return c=u=r=t,this},disabled:function(){return!c},lock:function(){return u=t,r||f.disable(),this},locked:function(){return!u},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!c||o&&!u||(i?u.push(t):p(t)),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},Y.extend({Deferred:function(e){var t=[["resolve","done",Y.Callbacks("once memory"),"resolved"],["reject","fail",Y.Callbacks("once memory"),"rejected"],["notify","progress",Y.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return o.done(arguments).fail(arguments),this},then:function(){var e=arguments;return Y.Deferred(function(n){Y.each(t,function(t,r){var i=r[0],a=e[t];o[r[1]](Y.isFunction(a)?function(){var e=a.apply(this,arguments);e&&Y.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[i+"With"](this===o?n:this,[e])}:n[i])}),e=null}).promise()},promise:function(e){return null!=e?Y.extend(e,r):r}},o={};return r.pipe=r.then,Y.each(t,function(e,i){var a=i[2],s=i[3];r[i[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),o[i[0]]=a.fire,o[i[0]+"With"]=a.fireWith}),r.promise(o),e&&e.call(o,o),o},when:function(e){var t,n,r,o=0,i=X.call(arguments),a=i.length,s=1!==a||e&&Y.isFunction(e.promise)?a:0,l=1===s?e:Y.Deferred(),c=function(e,n,r){return function(o){n[e]=this,r[e]=arguments.length>1?X.call(arguments):o,r===t?l.notifyWith(n,r):--s||l.resolveWith(n,r)}};if(a>1)for(t=Array(a),n=Array(a),r=Array(a);a>o;o++)i[o]&&Y.isFunction(i[o].promise)?i[o].promise().done(c(o,r,i)).fail(l.reject).progress(c(o,n,t)):--s;return s||l.resolveWith(r,i),l.promise()}}),Y.support=function(){var n,r,o,i,a,s,l,c,u,p,f,d=$.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
    a",r=d.getElementsByTagName("*"),o=d.getElementsByTagName("a")[0],!r||!o||!r.length)return{};i=$.createElement("select"),a=i.appendChild($.createElement("option")),s=d.getElementsByTagName("input")[0],o.style.cssText="top:1px;float:left;opacity:.5",n={leadingWhitespace:3===d.firstChild.nodeType,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/top/.test(o.getAttribute("style")),hrefNormalized:"/a"===o.getAttribute("href"),opacity:/^0.5/.test(o.style.opacity),cssFloat:!!o.style.cssFloat,checkOn:"on"===s.value,optSelected:a.selected,getSetAttribute:"t"!==d.className,enctype:!!$.createElement("form").enctype,html5Clone:"<:nav>"!==$.createElement("nav").cloneNode(!0).outerHTML,boxModel:"CSS1Compat"===$.compatMode,submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},s.checked=!0,n.noCloneChecked=s.cloneNode(!0).checked,i.disabled=!0,n.optDisabled=!a.disabled;try{delete d.test}catch(h){n.deleteExpando=!1}if(!d.addEventListener&&d.attachEvent&&d.fireEvent&&(d.attachEvent("onclick",f=function(){n.noCloneEvent=!1}),d.cloneNode(!0).fireEvent("onclick"),d.detachEvent("onclick",f)),s=$.createElement("input"),s.value="t",s.setAttribute("type","radio"),n.radioValue="t"===s.value,s.setAttribute("checked","checked"),s.setAttribute("name","t"),d.appendChild(s),l=$.createDocumentFragment(),l.appendChild(d.lastChild),n.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,n.appendChecked=s.checked,l.removeChild(s),l.appendChild(d),d.attachEvent)for(u in{submit:!0,change:!0,focusin:!0})c="on"+u,p=c in d,p||(d.setAttribute(c,"return;"),p="function"==typeof d[c]),n[u+"Bubbles"]=p;return Y(function(){var r,o,i,a,s="padding:0;margin:0;border:0;display:block;overflow:hidden;",l=$.getElementsByTagName("body")[0];l&&(r=$.createElement("div"),r.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",l.insertBefore(r,l.firstChild),o=$.createElement("div"),r.appendChild(o),o.innerHTML="
    t
    ",i=o.getElementsByTagName("td"),i[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===i[0].offsetHeight,i[0].style.display="",i[1].style.display="none",n.reliableHiddenOffsets=p&&0===i[0].offsetHeight,o.innerHTML="",o.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",n.boxSizing=4===o.offsetWidth,n.doesNotIncludeMarginInBodyOffset=1!==l.offsetTop,e.getComputedStyle&&(n.pixelPosition="1%"!==(e.getComputedStyle(o,null)||{}).top,n.boxSizingReliable="4px"===(e.getComputedStyle(o,null)||{width:"4px"}).width,a=$.createElement("div"),a.style.cssText=o.style.cssText=s,a.style.marginRight=a.style.width="0",o.style.width="1px",o.appendChild(a),n.reliableMarginRight=!parseFloat((e.getComputedStyle(a,null)||{}).marginRight)),o.style.zoom!==t&&(o.innerHTML="",o.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",n.inlineBlockNeedsLayout=3===o.offsetWidth,o.style.display="block",o.style.overflow="visible",o.innerHTML="
    ",o.firstChild.style.width="5px",n.shrinkWrapBlocks=3!==o.offsetWidth,r.style.zoom=1),l.removeChild(r),r=o=i=a=null)}),l.removeChild(d),r=o=i=a=s=l=d=null,n}();var gt=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,mt=/([A-Z])/g;Y.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(Y.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?Y.cache[e[Y.expando]]:e[Y.expando],!!e&&!o(e)},data:function(e,n,r,o){if(Y.acceptData(e)){var i,a,s=Y.expando,l="string"==typeof n,c=e.nodeType,u=c?Y.cache:e,p=c?e[s]:e[s]&&s;if(p&&u[p]&&(o||u[p].data)||!l||r!==t)return p||(c?e[s]=p=Y.deletedIds.pop()||Y.guid++:p=s),u[p]||(u[p]={},c||(u[p].toJSON=Y.noop)),("object"==typeof n||"function"==typeof n)&&(o?u[p]=Y.extend(u[p],n):u[p].data=Y.extend(u[p].data,n)),i=u[p],o||(i.data||(i.data={}),i=i.data),r!==t&&(i[Y.camelCase(n)]=r),l?(a=i[n],null==a&&(a=i[Y.camelCase(n)])):a=i,a}},removeData:function(e,t,n){if(Y.acceptData(e)){var r,i,a,s=e.nodeType,l=s?Y.cache:e,c=s?e[Y.expando]:Y.expando;if(l[c]){if(t&&(r=n?l[c]:l[c].data)){Y.isArray(t)||(t in r?t=[t]:(t=Y.camelCase(t),t=t in r?[t]:t.split(" ")));for(i=0,a=t.length;a>i;i++)delete r[t[i]];if(!(n?o:Y.isEmptyObject)(r))return}(n||(delete l[c].data,o(l[c])))&&(s?Y.cleanData([e],!0):Y.support.deleteExpando||l!=l.window?delete l[c]:l[c]=null)}}},_data:function(e,t,n){return Y.data(e,t,n,!0)},acceptData:function(e){var t=e.nodeName&&Y.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),Y.fn.extend({data:function(e,n){var o,i,a,s,l,c=this[0],u=0,p=null;if(e===t){if(this.length&&(p=Y.data(c),1===c.nodeType&&!Y._data(c,"parsedAttrs"))){for(a=c.attributes,l=a.length;l>u;u++)s=a[u].name,s.indexOf("data-")||(s=Y.camelCase(s.substring(5)),r(c,s,p[s]));Y._data(c,"parsedAttrs",!0)}return p}return"object"==typeof e?this.each(function(){Y.data(this,e)}):(o=e.split(".",2),o[1]=o[1]?"."+o[1]:"",i=o[1]+"!",Y.access(this,function(n){return n===t?(p=this.triggerHandler("getData"+i,[o[0]]),p===t&&c&&(p=Y.data(c,e),p=r(c,e,p)),p===t&&o[1]?this.data(o[0]):p):(o[1]=n,this.each(function(){var t=Y(this);t.triggerHandler("setData"+i,o),Y.data(this,e,n),t.triggerHandler("changeData"+i,o)}),t)},null,n,arguments.length>1,null,!1))},removeData:function(e){return this.each(function(){Y.removeData(this,e)})}}),Y.extend({queue:function(e,n,r){var o;return e?(n=(n||"fx")+"queue",o=Y._data(e,n),r&&(!o||Y.isArray(r)?o=Y._data(e,n,Y.makeArray(r)):o.push(r)),o||[]):t},dequeue:function(e,t){t=t||"fx";var n=Y.queue(e,t),r=n.length,o=n.shift(),i=Y._queueHooks(e,t),a=function(){Y.dequeue(e,t)};"inprogress"===o&&(o=n.shift(),r--),o&&("fx"===t&&n.unshift("inprogress"),delete i.stop,o.call(e,a,i)),!r&&i&&i.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Y._data(e,n)||Y._data(e,n,{empty:Y.Callbacks("once memory").add(function(){Y.removeData(e,t+"queue",!0),Y.removeData(e,n,!0)})})}}),Y.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?Y.queue(this[0],e):n===t?this:this.each(function(){var t=Y.queue(this,e,n);Y._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&Y.dequeue(this,e)})},dequeue:function(e){return this.each(function(){Y.dequeue(this,e)})},delay:function(e,t){return e=Y.fx?Y.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,o=1,i=Y.Deferred(),a=this,s=this.length,l=function(){--o||i.resolveWith(a,[a])};for("string"!=typeof e&&(n=e,e=t),e=e||"fx";s--;)r=Y._data(a[s],e+"queueHooks"),r&&r.empty&&(o++,r.empty.add(l));return l(),i.promise(n)}});var yt,vt,bt,xt=/[\t\r\n]/g,wt=/\r/g,_t=/^(?:button|input)$/i,Ct=/^(?:button|input|object|select|textarea)$/i,kt=/^a(?:rea|)$/i,At=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,Tt=Y.support.getSetAttribute;Y.fn.extend({attr:function(e,t){return Y.access(this,Y.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){Y.removeAttr(this,e)})},prop:function(e,t){return Y.access(this,Y.prop,e,t,arguments.length>1)},removeProp:function(e){return e=Y.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,o,i,a,s;if(Y.isFunction(e))return this.each(function(t){Y(this).addClass(e.call(this,t,this.className))});if(e&&"string"==typeof e)for(t=e.split(tt),n=0,r=this.length;r>n;n++)if(o=this[n],1===o.nodeType)if(o.className||1!==t.length){for(i=" "+o.className+" ",a=0,s=t.length;s>a;a++)0>i.indexOf(" "+t[a]+" ")&&(i+=t[a]+" ");o.className=Y.trim(i)}else o.className=e;return this},removeClass:function(e){var n,r,o,i,a,s,l;if(Y.isFunction(e))return this.each(function(t){Y(this).removeClass(e.call(this,t,this.className))});if(e&&"string"==typeof e||e===t)for(n=(e||"").split(tt),s=0,l=this.length;l>s;s++)if(o=this[s],1===o.nodeType&&o.className){for(r=(" "+o.className+" ").replace(xt," "),i=0,a=n.length;a>i;i++)for(;r.indexOf(" "+n[i]+" ")>=0;)r=r.replace(" "+n[i]+" "," ");o.className=e?Y.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return Y.isFunction(e)?this.each(function(n){Y(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n)for(var o,i=0,a=Y(this),s=t,l=e.split(tt);o=l[i++];)s=r?s:!a.hasClass(o),a[s?"addClass":"removeClass"](o);else("undefined"===n||"boolean"===n)&&(this.className&&Y._data(this,"__className__",this.className),this.className=this.className||e===!1?"":Y._data(this,"__className__")||"")})},hasClass:function(e){for(var t=" "+e+" ",n=0,r=this.length;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(xt," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,o,i=this[0];{if(arguments.length)return o=Y.isFunction(e),this.each(function(r){var i,a=Y(this);1===this.nodeType&&(i=o?e.call(this,r,a.val()):e,null==i?i="":"number"==typeof i?i+="":Y.isArray(i)&&(i=Y.map(i,function(e){return null==e?"":e+""})),n=Y.valHooks[this.type]||Y.valHooks[this.nodeName.toLowerCase()],n&&"set"in n&&n.set(this,i,"value")!==t||(this.value=i))});if(i)return n=Y.valHooks[i.type]||Y.valHooks[i.nodeName.toLowerCase()],n&&"get"in n&&(r=n.get(i,"value"))!==t?r:(r=i.value,"string"==typeof r?r.replace(wt,""):null==r?"":r)}}}),Y.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){for(var t,n,r=e.options,o=e.selectedIndex,i="select-one"===e.type||0>o,a=i?null:[],s=i?o+1:r.length,l=0>o?s:i?o:0;s>l;l++)if(n=r[l],!(!n.selected&&l!==o||(Y.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&Y.nodeName(n.parentNode,"optgroup"))){if(t=Y(n).val(),i)return t;a.push(t)}return a},set:function(e,t){var n=Y.makeArray(t);return Y(e).find("option").each(function(){this.selected=Y.inArray(Y(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attrFn:{},attr:function(e,n,r,o){var i,a,s,l=e.nodeType;if(e&&3!==l&&8!==l&&2!==l)return o&&Y.isFunction(Y.fn[n])?Y(e)[n](r):e.getAttribute===t?Y.prop(e,n,r):(s=1!==l||!Y.isXMLDoc(e),s&&(n=n.toLowerCase(),a=Y.attrHooks[n]||(At.test(n)?vt:yt)),r!==t?null===r?(Y.removeAttr(e,n),t):a&&"set"in a&&s&&(i=a.set(e,r,n))!==t?i:(e.setAttribute(n,r+""),r):a&&"get"in a&&s&&null!==(i=a.get(e,n))?i:(i=e.getAttribute(n),null===i?t:i))},removeAttr:function(e,t){var n,r,o,i,a=0;if(t&&1===e.nodeType)for(r=t.split(tt);r.length>a;a++)o=r[a],o&&(n=Y.propFix[o]||o,i=At.test(o),i||Y.attr(e,o,""),e.removeAttribute(Tt?o:n),i&&n in e&&(e[n]=!1))},attrHooks:{type:{set:function(e,t){if(_t.test(e.nodeName)&&e.parentNode)Y.error("type property can't be changed");else if(!Y.support.radioValue&&"radio"===t&&Y.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}},value:{get:function(e,t){return yt&&Y.nodeName(e,"button")?yt.get(e,t):t in e?e.value:null},set:function(e,n,r){return yt&&Y.nodeName(e,"button")?yt.set(e,n,r):(e.value=n,t)}}},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(e,n,r){var o,i,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!Y.isXMLDoc(e),a&&(n=Y.propFix[n]||n,i=Y.propHooks[n]),r!==t?i&&"set"in i&&(o=i.set(e,r,n))!==t?o:e[n]=r:i&&"get"in i&&null!==(o=i.get(e,n))?o:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):Ct.test(e.nodeName)||kt.test(e.nodeName)&&e.href?0:t}}}}),vt={get:function(e,n){var r,o=Y.prop(e,n);return o===!0||"boolean"!=typeof o&&(r=e.getAttributeNode(n))&&r.nodeValue!==!1?n.toLowerCase():t},set:function(e,t,n){var r;return t===!1?Y.removeAttr(e,n):(r=Y.propFix[n]||n,r in e&&(e[r]=!0),e.setAttribute(n,n.toLowerCase())),n}},Tt||(bt={name:!0,id:!0,coords:!0},yt=Y.valHooks.button={get:function(e,n){var r;return r=e.getAttributeNode(n),r&&(bt[n]?""!==r.value:r.specified)?r.value:t},set:function(e,t,n){var r=e.getAttributeNode(n);return r||(r=$.createAttribute(n),e.setAttributeNode(r)),r.value=t+""}},Y.each(["width","height"],function(e,n){Y.attrHooks[n]=Y.extend(Y.attrHooks[n],{set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}})}),Y.attrHooks.contenteditable={get:yt.get,set:function(e,t,n){""===t&&(t="false"),yt.set(e,t,n) +}}),Y.support.hrefNormalized||Y.each(["href","src","width","height"],function(e,n){Y.attrHooks[n]=Y.extend(Y.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return null===r?t:r}})}),Y.support.style||(Y.attrHooks.style={get:function(e){return e.style.cssText.toLowerCase()||t},set:function(e,t){return e.style.cssText=t+""}}),Y.support.optSelected||(Y.propHooks.selected=Y.extend(Y.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),Y.support.enctype||(Y.propFix.enctype="encoding"),Y.support.checkOn||Y.each(["radio","checkbox"],function(){Y.valHooks[this]={get:function(e){return null===e.getAttribute("value")?"on":e.value}}}),Y.each(["radio","checkbox"],function(){Y.valHooks[this]=Y.extend(Y.valHooks[this],{set:function(e,n){return Y.isArray(n)?e.checked=Y.inArray(Y(e).val(),n)>=0:t}})});var Et=/^(?:textarea|input|select)$/i,Ft=/^([^\.]*|)(?:\.(.+)|)$/,Nt=/(?:^|\s)hover(\.\S+|)\b/,St=/^key/,jt=/^(?:mouse|contextmenu)|click/,Rt=/^(?:focusinfocus|focusoutblur)$/,Ot=function(e){return Y.event.special.hover?e:e.replace(Nt,"mouseenter$1 mouseleave$1")};Y.event={add:function(e,n,r,o,i){var a,s,l,c,u,p,f,d,h,g,m;if(3!==e.nodeType&&8!==e.nodeType&&n&&r&&(a=Y._data(e))){for(r.handler&&(h=r,r=h.handler,i=h.selector),r.guid||(r.guid=Y.guid++),l=a.events,l||(a.events=l={}),s=a.handle,s||(a.handle=s=function(e){return Y===t||e&&Y.event.triggered===e.type?t:Y.event.dispatch.apply(s.elem,arguments)},s.elem=e),n=Y.trim(Ot(n)).split(" "),c=0;n.length>c;c++)u=Ft.exec(n[c])||[],p=u[1],f=(u[2]||"").split(".").sort(),m=Y.event.special[p]||{},p=(i?m.delegateType:m.bindType)||p,m=Y.event.special[p]||{},d=Y.extend({type:p,origType:u[1],data:o,handler:r,guid:r.guid,selector:i,needsContext:i&&Y.expr.match.needsContext.test(i),namespace:f.join(".")},h),g=l[p],g||(g=l[p]=[],g.delegateCount=0,m.setup&&m.setup.call(e,o,f,s)!==!1||(e.addEventListener?e.addEventListener(p,s,!1):e.attachEvent&&e.attachEvent("on"+p,s))),m.add&&(m.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),i?g.splice(g.delegateCount++,0,d):g.push(d),Y.event.global[p]=!0;e=null}},global:{},remove:function(e,t,n,r,o){var i,a,s,l,c,u,p,f,d,h,g,m=Y.hasData(e)&&Y._data(e);if(m&&(f=m.events)){for(t=Y.trim(Ot(t||"")).split(" "),i=0;t.length>i;i++)if(a=Ft.exec(t[i])||[],s=l=a[1],c=a[2],s){for(d=Y.event.special[s]||{},s=(r?d.delegateType:d.bindType)||s,h=f[s]||[],u=h.length,c=c?RegExp("(^|\\.)"+c.split(".").sort().join("\\.(?:.*\\.|)")+"(\\.|$)"):null,p=0;h.length>p;p++)g=h[p],!o&&l!==g.origType||n&&n.guid!==g.guid||c&&!c.test(g.namespace)||r&&r!==g.selector&&("**"!==r||!g.selector)||(h.splice(p--,1),g.selector&&h.delegateCount--,d.remove&&d.remove.call(e,g));0===h.length&&u!==h.length&&(d.teardown&&d.teardown.call(e,c,m.handle)!==!1||Y.removeEvent(e,s,m.handle),delete f[s])}else for(s in f)Y.event.remove(e,s+t[i],n,r,!0);Y.isEmptyObject(f)&&(delete m.handle,Y.removeData(e,"events",!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(n,r,o,i){if(!o||3!==o.nodeType&&8!==o.nodeType){var a,s,l,c,u,p,f,d,h,g,m=n.type||n,y=[];if(!Rt.test(m+Y.event.triggered)&&(m.indexOf("!")>=0&&(m=m.slice(0,-1),s=!0),m.indexOf(".")>=0&&(y=m.split("."),m=y.shift(),y.sort()),o&&!Y.event.customEvent[m]||Y.event.global[m]))if(n="object"==typeof n?n[Y.expando]?n:new Y.Event(m,n):new Y.Event(m),n.type=m,n.isTrigger=!0,n.exclusive=s,n.namespace=y.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+y.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,p=0>m.indexOf(":")?"on"+m:"",o){if(n.result=t,n.target||(n.target=o),r=null!=r?Y.makeArray(r):[],r.unshift(n),f=Y.event.special[m]||{},!f.trigger||f.trigger.apply(o,r)!==!1){if(h=[[o,f.bindType||m]],!i&&!f.noBubble&&!Y.isWindow(o)){for(g=f.delegateType||m,c=Rt.test(g+m)?o:o.parentNode,u=o;c;c=c.parentNode)h.push([c,g]),u=c;u===(o.ownerDocument||$)&&h.push([u.defaultView||u.parentWindow||e,g])}for(l=0;h.length>l&&!n.isPropagationStopped();l++)c=h[l][0],n.type=h[l][1],d=(Y._data(c,"events")||{})[n.type]&&Y._data(c,"handle"),d&&d.apply(c,r),d=p&&c[p],d&&Y.acceptData(c)&&d.apply&&d.apply(c,r)===!1&&n.preventDefault();return n.type=m,i||n.isDefaultPrevented()||f._default&&f._default.apply(o.ownerDocument,r)!==!1||"click"===m&&Y.nodeName(o,"a")||!Y.acceptData(o)||p&&o[m]&&("focus"!==m&&"blur"!==m||0!==n.target.offsetWidth)&&!Y.isWindow(o)&&(u=o[p],u&&(o[p]=null),Y.event.triggered=m,o[m](),Y.event.triggered=t,u&&(o[p]=u)),n.result}}else{a=Y.cache;for(l in a)a[l].events&&a[l].events[m]&&Y.event.trigger(n,r,a[l].handle.elem,!0)}}},dispatch:function(n){n=Y.event.fix(n||e.event);var r,o,i,a,s,l,c,u,p,f=(Y._data(this,"events")||{})[n.type]||[],d=f.delegateCount,h=X.call(arguments),g=!n.exclusive&&!n.namespace,m=Y.event.special[n.type]||{},y=[];if(h[0]=n,n.delegateTarget=this,!m.preDispatch||m.preDispatch.call(this,n)!==!1){if(d&&(!n.button||"click"!==n.type))for(i=n.target;i!=this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==n.type){for(s={},c=[],r=0;d>r;r++)u=f[r],p=u.selector,s[p]===t&&(s[p]=u.needsContext?Y(p,this).index(i)>=0:Y.find(p,this,null,[i]).length),s[p]&&c.push(u);c.length&&y.push({elem:i,matches:c})}for(f.length>d&&y.push({elem:this,matches:f.slice(d)}),r=0;y.length>r&&!n.isPropagationStopped();r++)for(l=y[r],n.currentTarget=l.elem,o=0;l.matches.length>o&&!n.isImmediatePropagationStopped();o++)u=l.matches[o],(g||!n.namespace&&!u.namespace||n.namespace_re&&n.namespace_re.test(u.namespace))&&(n.data=u.data,n.handleObj=u,a=((Y.event.special[u.origType]||{}).handle||u.handler).apply(l.elem,h),a!==t&&(n.result=a,a===!1&&(n.preventDefault(),n.stopPropagation())));return m.postDispatch&&m.postDispatch.call(this,n),n.result}},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(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,o,i,a=n.button,s=n.fromElement;return null==e.pageX&&null!=n.clientX&&(r=e.target.ownerDocument||$,o=r.documentElement,i=r.body,e.pageX=n.clientX+(o&&o.scrollLeft||i&&i.scrollLeft||0)-(o&&o.clientLeft||i&&i.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||i&&i.scrollTop||0)-(o&&o.clientTop||i&&i.clientTop||0)),!e.relatedTarget&&s&&(e.relatedTarget=s===e.target?n.toElement:s),e.which||a===t||(e.which=1&a?1:2&a?3:4&a?2:0),e}},fix:function(e){if(e[Y.expando])return e;var t,n,r=e,o=Y.event.fixHooks[e.type]||{},i=o.props?this.props.concat(o.props):this.props;for(e=Y.Event(r),t=i.length;t;)n=i[--t],e[n]=r[n];return e.target||(e.target=r.srcElement||$),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,o.filter?o.filter(e,r):e},special:{load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(e,t,n){Y.isWindow(this)&&(this.onbeforeunload=n)},teardown:function(e,t){this.onbeforeunload===t&&(this.onbeforeunload=null)}}},simulate:function(e,t,n,r){var o=Y.extend(new Y.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?Y.event.trigger(o,null,t):Y.event.dispatch.call(t,o),o.isDefaultPrevented()&&n.preventDefault()}},Y.event.handle=Y.event.dispatch,Y.removeEvent=$.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,n,r){var o="on"+n;e.detachEvent&&(e[o]===t&&(e[o]=null),e.detachEvent(o,r))},Y.Event=function(e,n){return this instanceof Y.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?a:i):this.type=e,n&&Y.extend(this,n),this.timeStamp=e&&e.timeStamp||Y.now(),this[Y.expando]=!0,t):new Y.Event(e,n)},Y.Event.prototype={preventDefault:function(){this.isDefaultPrevented=a;var e=this.originalEvent;e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=a;var e=this.originalEvent;e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=a,this.stopPropagation()},isDefaultPrevented:i,isPropagationStopped:i,isImmediatePropagationStopped:i},Y.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){Y.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,o=e.relatedTarget,i=e.handleObj;return i.selector,(!o||o!==r&&!Y.contains(r,o))&&(e.type=i.origType,n=i.handler.apply(this,arguments),e.type=t),n}}}),Y.support.submitBubbles||(Y.event.special.submit={setup:function(){return Y.nodeName(this,"form")?!1:(Y.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=Y.nodeName(n,"input")||Y.nodeName(n,"button")?n.form:t;r&&!Y._data(r,"_submit_attached")&&(Y.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),Y._data(r,"_submit_attached",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&Y.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return Y.nodeName(this,"form")?!1:(Y.event.remove(this,"._submit"),t)}}),Y.support.changeBubbles||(Y.event.special.change={setup:function(){return Et.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(Y.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),Y.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),Y.event.simulate("change",this,e,!0)})),!1):(Y.event.add(this,"beforeactivate._change",function(e){var t=e.target;Et.test(t.nodeName)&&!Y._data(t,"_change_attached")&&(Y.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||Y.event.simulate("change",this.parentNode,e,!0)}),Y._data(t,"_change_attached",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return Y.event.remove(this,"._change"),!Et.test(this.nodeName)}}),Y.support.focusinBubbles||Y.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){Y.event.simulate(t,e.target,Y.event.fix(e),!0)};Y.event.special[t]={setup:function(){0===n++&&$.addEventListener(e,r,!0)},teardown:function(){0===--n&&$.removeEventListener(e,r,!0)}}}),Y.fn.extend({on:function(e,n,r,o,a){var s,l;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(l in e)this.on(l,n,r,e[l],a);return this}if(null==r&&null==o?(o=n,r=n=t):null==o&&("string"==typeof n?(o=r,r=t):(o=r,r=n,n=t)),o===!1)o=i;else if(!o)return this;return 1===a&&(s=o,o=function(e){return Y().off(e),s.apply(this,arguments)},o.guid=s.guid||(s.guid=Y.guid++)),this.each(function(){Y.event.add(this,e,o,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var o,a;if(e&&e.preventDefault&&e.handleObj)return o=e.handleObj,Y(e.delegateTarget).off(o.namespace?o.origType+"."+o.namespace:o.origType,o.selector,o.handler),this;if("object"==typeof e){for(a in e)this.off(a,n,e[a]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=i),this.each(function(){Y.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},live:function(e,t,n){return Y(this.context).on(e,this.selector,t,n),this},die:function(e,t){return Y(this.context).off(e,this.selector||"**",t),this},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},trigger:function(e,t){return this.each(function(){Y.event.trigger(e,t,this)})},triggerHandler:function(e,n){return this[0]?Y.event.trigger(e,n,this[0],!0):t},toggle:function(e){var t=arguments,n=e.guid||Y.guid++,r=0,o=function(n){var o=(Y._data(this,"lastToggle"+e.guid)||0)%r;return Y._data(this,"lastToggle"+e.guid,o+1),n.preventDefault(),t[o].apply(this,arguments)||!1};for(o.guid=n;t.length>r;)t[r++].guid=n;return this.click(o)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),Y.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(e,t){Y.fn[t]=function(e,n){return null==n&&(n=e,e=null),arguments.length>0?this.on(t,null,e,n):this.trigger(t)},St.test(t)&&(Y.event.fixHooks[t]=Y.event.keyHooks),jt.test(t)&&(Y.event.fixHooks[t]=Y.event.mouseHooks)}),/*! + * Sizzle CSS Selector Engine + * Copyright 2012 jQuery Foundation and other contributors + * Released under the MIT license + * http://sizzlejs.com/ + */ +function(e,t){function n(e,t,n,r){n=n||[],t=t||j;var o,i,a,s,l=t.nodeType;if(!e||"string"!=typeof e)return n;if(1!==l&&9!==l)return[];if(a=w(t),!a&&!r&&(o=nt.exec(e)))if(s=o[1]){if(9===l){if(i=t.getElementById(s),!i||!i.parentNode)return n;if(i.id===s)return n.push(i),n}else if(t.ownerDocument&&(i=t.ownerDocument.getElementById(s))&&_(t,i)&&i.id===s)return n.push(i),n}else{if(o[2])return I.apply(n,P.call(t.getElementsByTagName(e),0)),n;if((s=o[3])&&ft&&t.getElementsByClassName)return I.apply(n,P.call(t.getElementsByClassName(s),0)),n}return g(e.replace(G,"$1"),t,n,r,a)}function r(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function o(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function i(e){return L(function(t){return t=+t,L(function(n,r){for(var o,i=e([],n.length,t),a=i.length;a--;)n[o=i[a]]&&(n[o]=!(r[o]=n[o]))})})}function a(e,t,n){if(e===t)return n;for(var r=e.nextSibling;r;){if(r===t)return-1;r=r.nextSibling}return 1}function s(e,t){var r,o,i,a,s,l,c,u=q[N][e+" "];if(u)return t?0:u.slice(0);for(s=e,l=[],c=b.preFilter;s;){(!r||(o=Z.exec(s)))&&(o&&(s=s.slice(o[0].length)||s),l.push(i=[])),r=!1,(o=et.exec(s))&&(i.push(r=new S(o.shift())),s=s.slice(r.length),r.type=o[0].replace(G," "));for(a in b.filter)!(o=st[a].exec(s))||c[a]&&!(o=c[a](o))||(i.push(r=new S(o.shift())),s=s.slice(r.length),r.type=a,r.matches=o);if(!r)break}return t?s.length:s?n.error(e):q(e,l).slice(0)}function l(e,t,n){var r=t.dir,o=n&&"parentNode"===t.dir,i=M++;return t.first?function(t,n,i){for(;t=t[r];)if(o||1===t.nodeType)return e(t,n,i)}:function(t,n,a){if(a){for(;t=t[r];)if((o||1===t.nodeType)&&e(t,n,a))return t}else for(var s,l=O+" "+i+" ",c=l+y;t=t[r];)if(o||1===t.nodeType){if((s=t[N])===c)return t.sizset;if("string"==typeof s&&0===s.indexOf(l)){if(t.sizset)return t}else{if(t[N]=c,e(t,n,a))return t.sizset=!0,t;t.sizset=!1}}}}function c(e){return e.length>1?function(t,n,r){for(var o=e.length;o--;)if(!e[o](t,n,r))return!1;return!0}:e[0]}function u(e,t,n,r,o){for(var i,a=[],s=0,l=e.length,c=null!=t;l>s;s++)(i=e[s])&&(!n||n(i,r,o))&&(a.push(i),c&&t.push(s));return a}function p(e,t,n,r,o,i){return r&&!r[N]&&(r=p(r)),o&&!o[N]&&(o=p(o,i)),L(function(i,a,s,l){var c,p,f,d=[],g=[],m=a.length,y=i||h(t||"*",s.nodeType?[s]:s,[]),v=!e||!i&&t?y:u(y,d,e,s,l),b=n?o||(i?e:m||r)?[]:a:v;if(n&&n(v,b,s,l),r)for(c=u(b,g),r(c,[],s,l),p=c.length;p--;)(f=c[p])&&(b[g[p]]=!(v[g[p]]=f));if(i){if(o||e){if(o){for(c=[],p=b.length;p--;)(f=b[p])&&c.push(v[p]=f);o(null,b=[],c,l)}for(p=b.length;p--;)(f=b[p])&&(c=o?D.call(i,f):d[p])>-1&&(i[c]=!(a[c]=f))}}else b=u(b===a?b.splice(m,b.length):b),o?o(null,a,b,l):I.apply(a,b)})}function f(e){for(var t,n,r,o=e.length,i=b.relative[e[0].type],a=i||b.relative[" "],s=i?1:0,u=l(function(e){return e===t},a,!0),d=l(function(e){return D.call(t,e)>-1},a,!0),h=[function(e,n,r){return!i&&(r||n!==T)||((t=n).nodeType?u(e,n,r):d(e,n,r))}];o>s;s++)if(n=b.relative[e[s].type])h=[l(c(h),n)];else{if(n=b.filter[e[s].type].apply(null,e[s].matches),n[N]){for(r=++s;o>r&&!b.relative[e[r].type];r++);return p(s>1&&c(h),s>1&&e.slice(0,s-1).join("").replace(G,"$1"),n,r>s&&f(e.slice(s,r)),o>r&&f(e=e.slice(r)),o>r&&e.join(""))}h.push(n)}return c(h)}function d(e,t){var r=t.length>0,o=e.length>0,i=function(a,s,l,c,p){var f,d,h,g=[],m=0,v="0",x=a&&[],w=null!=p,_=T,C=a||o&&b.find.TAG("*",p&&s.parentNode||s),k=O+=null==_?1:Math.E;for(w&&(T=s!==j&&s,y=i.el);null!=(f=C[v]);v++){if(o&&f){for(d=0;h=e[d];d++)if(h(f,s,l)){c.push(f);break}w&&(O=k,y=++i.el)}r&&((f=!h&&f)&&m--,a&&x.push(f))}if(m+=v,r&&v!==m){for(d=0;h=t[d];d++)h(x,g,s,l);if(a){if(m>0)for(;v--;)x[v]||g[v]||(g[v]=H.call(c));g=u(g)}I.apply(c,g),w&&!a&&g.length>0&&m+t.length>1&&n.uniqueSort(c)}return w&&(O=k,T=_),x};return i.el=0,r?L(i):i}function h(e,t,r){for(var o=0,i=t.length;i>o;o++)n(e,t[o],r);return r}function g(e,t,n,r,o){var i,a,l,c,u,p=s(e);if(p.length,!r&&1===p.length){if(a=p[0]=p[0].slice(0),a.length>2&&"ID"===(l=a[0]).type&&9===t.nodeType&&!o&&b.relative[a[1].type]){if(t=b.find.ID(l.matches[0].replace(at,""),t,o)[0],!t)return n;e=e.slice(a.shift().length)}for(i=st.POS.test(e)?-1:a.length-1;i>=0&&(l=a[i],!b.relative[c=l.type]);i--)if((u=b.find[c])&&(r=u(l.matches[0].replace(at,""),rt.test(a[0].type)&&t.parentNode||t,o))){if(a.splice(i,1),e=r.length&&a.join(""),!e)return I.apply(n,P.call(r,0)),n;break}}return C(e,p)(r,t,o,n,rt.test(e)),n}function m(){}var y,v,b,x,w,_,C,k,A,T,E=!0,F="undefined",N=("sizcache"+Math.random()).replace(".",""),S=String,j=e.document,R=j.documentElement,O=0,M=0,H=[].pop,I=[].push,P=[].slice,D=[].indexOf||function(e){for(var t=0,n=this.length;n>t;t++)if(this[t]===e)return t;return-1},L=function(e,t){return e[N]=null==t||t,e},B=function(){var e={},t=[];return L(function(n,r){return t.push(n)>b.cacheLength&&delete e[t.shift()],e[n+" "]=r},e)},$=B(),q=B(),W=B(),U="[\\x20\\t\\r\\n\\f]",Q="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",z=Q.replace("w","w#"),X="([*^$|!~]?=)",J="\\["+U+"*("+Q+")"+U+"*(?:"+X+U+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+z+")|)|)"+U+"*\\]",V=":("+Q+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+J+")|[^:]|\\\\.)*|.*))\\)|)",K=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+U+"*((?:-\\d)?\\d*)"+U+"*\\)|)(?=[^-]|$)",G=RegExp("^"+U+"+|((?:^|[^\\\\])(?:\\\\.)*)"+U+"+$","g"),Z=RegExp("^"+U+"*,"+U+"*"),et=RegExp("^"+U+"*([\\x20\\t\\r\\n\\f>+~])"+U+"*"),tt=RegExp(V),nt=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,rt=/[\x20\t\r\n\f]*[+~]/,ot=/h\d/i,it=/input|select|textarea|button/i,at=/\\(?!\\)/g,st={ID:RegExp("^#("+Q+")"),CLASS:RegExp("^\\.("+Q+")"),NAME:RegExp("^\\[name=['\"]?("+Q+")['\"]?\\]"),TAG:RegExp("^("+Q.replace("w","w*")+")"),ATTR:RegExp("^"+J),PSEUDO:RegExp("^"+V),POS:RegExp(K,"i"),CHILD:RegExp("^:(only|nth|first|last)-child(?:\\("+U+"*(even|odd|(([+-]|)(\\d*)n|)"+U+"*(?:([+-]|)"+U+"*(\\d+)|))"+U+"*\\)|)","i"),needsContext:RegExp("^"+U+"*[>+~]|"+K,"i")},lt=function(e){var t=j.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}},ct=lt(function(e){return e.appendChild(j.createComment("")),!e.getElementsByTagName("*").length}),ut=lt(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==F&&"#"===e.firstChild.getAttribute("href")}),pt=lt(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return"boolean"!==t&&"string"!==t}),ft=lt(function(e){return e.innerHTML="",e.getElementsByClassName&&e.getElementsByClassName("e").length?(e.lastChild.className="e",2===e.getElementsByClassName("e").length):!1}),dt=lt(function(e){e.id=N+0,e.innerHTML="
    ",R.insertBefore(e,R.firstChild);var t=j.getElementsByName&&j.getElementsByName(N).length===2+j.getElementsByName(N+0).length;return v=!j.getElementById(N),R.removeChild(e),t});try{P.call(R.childNodes,0)[0].nodeType}catch(ht){P=function(e){for(var t,n=[];t=this[e];e++)n.push(t);return n}}n.matches=function(e,t){return n(e,null,null,t)},n.matchesSelector=function(e,t){return n(t,null,null,[e]).length>0},x=n.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=x(e)}else if(3===o||4===o)return e.nodeValue}else for(;t=e[r];r++)n+=x(t);return n},w=n.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},_=n.contains=R.contains?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!!(r&&1===r.nodeType&&n.contains&&n.contains(r))}:R.compareDocumentPosition?function(e,t){return t&&!!(16&e.compareDocumentPosition(t))}:function(e,t){for(;t=t.parentNode;)if(t===e)return!0;return!1},n.attr=function(e,t){var n,r=w(e);return r||(t=t.toLowerCase()),(n=b.attrHandle[t])?n(e):r||pt?e.getAttribute(t):(n=e.getAttributeNode(t),n?"boolean"==typeof e[t]?e[t]?t:null:n.specified?n.value:null:null)},b=n.selectors={cacheLength:50,createPseudo:L,match:st,attrHandle:ut?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},find:{ID:v?function(e,t,n){if(typeof t.getElementById!==F&&!n){var r=t.getElementById(e);return r&&r.parentNode?[r]:[]}}:function(e,n,r){if(typeof n.getElementById!==F&&!r){var o=n.getElementById(e);return o?o.id===e||typeof o.getAttributeNode!==F&&o.getAttributeNode("id").value===e?[o]:t:[]}},TAG:ct?function(e,n){return typeof n.getElementsByTagName!==F?n.getElementsByTagName(e):t}:function(e,t){var n=t.getElementsByTagName(e);if("*"===e){for(var r,o=[],i=0;r=n[i];i++)1===r.nodeType&&o.push(r);return o}return n},NAME:dt&&function(e,n){return typeof n.getElementsByName!==F?n.getElementsByName(name):t},CLASS:ft&&function(e,n,r){return typeof n.getElementsByClassName===F||r?t:n.getElementsByClassName(e)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(at,""),e[3]=(e[4]||e[5]||"").replace(at,""),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1]?(e[2]||n.error(e[0]),e[3]=+(e[3]?e[4]+(e[5]||1):2*("even"===e[2]||"odd"===e[2])),e[4]=+(e[6]+e[7]||"odd"===e[2])):e[2]&&n.error(e[0]),e},PSEUDO:function(e){var t,n;return st.CHILD.test(e[0])?null:(e[3]?e[2]=e[3]:(t=e[4])&&(tt.test(t)&&(n=s(t,!0))&&(n=t.indexOf(")",t.length-n)-t.length)&&(t=t.slice(0,n),e[0]=e[0].slice(0,n)),e[2]=t),e.slice(0,3))}},filter:{ID:v?function(e){return e=e.replace(at,""),function(t){return t.getAttribute("id")===e}}:function(e){return e=e.replace(at,""),function(t){var n=typeof t.getAttributeNode!==F&&t.getAttributeNode("id");return n&&n.value===e}},TAG:function(e){return"*"===e?function(){return!0}:(e=e.replace(at,"").toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=$[N][e+" "];return t||(t=RegExp("(^|"+U+")"+e+"("+U+"|$)"))&&$(e,function(e){return t.test(e.className||typeof e.getAttribute!==F&&e.getAttribute("class")||"")})},ATTR:function(e,t,r){return function(o){var i=n.attr(o,e);return null==i?"!="===t:t?(i+="","="===t?i===r:"!="===t?i!==r:"^="===t?r&&0===i.indexOf(r):"*="===t?r&&i.indexOf(r)>-1:"$="===t?r&&i.substr(i.length-r.length)===r:"~="===t?(" "+i+" ").indexOf(r)>-1:"|="===t?i===r||i.substr(0,r.length+1)===r+"-":!1):!0}},CHILD:function(e,t,n,r){return"nth"===e?function(e){var t,o,i=e.parentNode;if(1===n&&0===r)return!0;if(i)for(o=0,t=i.firstChild;t&&(1!==t.nodeType||(o++,e!==t));t=t.nextSibling);return o-=r,o===n||0===o%n&&o/n>=0}:function(t){var n=t;switch(e){case"only":case"first":for(;n=n.previousSibling;)if(1===n.nodeType)return!1;if("first"===e)return!0;n=t;case"last":for(;n=n.nextSibling;)if(1===n.nodeType)return!1;return!0}}},PSEUDO:function(e,t){var r,o=b.pseudos[e]||b.setFilters[e.toLowerCase()]||n.error("unsupported pseudo: "+e);return o[N]?o(t):o.length>1?(r=[e,e,"",t],b.setFilters.hasOwnProperty(e.toLowerCase())?L(function(e,n){for(var r,i=o(e,t),a=i.length;a--;)r=D.call(e,i[a]),e[r]=!(n[r]=i[a])}):function(e){return o(e,0,r)}):o}},pseudos:{not:L(function(e){var t=[],n=[],r=C(e.replace(G,"$1"));return r[N]?L(function(e,t,n,o){for(var i,a=r(e,null,o,[]),s=e.length;s--;)(i=a[s])&&(e[s]=!(t[s]=i))}):function(e,o,i){return t[0]=e,r(t,null,i,n),!n.pop()}}),has:L(function(e){return function(t){return n(e,t).length>0}}),contains:L(function(e){return function(t){return(t.textContent||t.innerText||x(t)).indexOf(e)>-1}}),enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},parent:function(e){return!b.pseudos.empty(e)},empty:function(e){var t;for(e=e.firstChild;e;){if(e.nodeName>"@"||3===(t=e.nodeType)||4===t)return!1;e=e.nextSibling}return!0},header:function(e){return ot.test(e.nodeName)},text:function(e){var t,n;return"input"===e.nodeName.toLowerCase()&&"text"===(t=e.type)&&(null==(n=e.getAttribute("type"))||n.toLowerCase()===t)},radio:r("radio"),checkbox:r("checkbox"),file:r("file"),password:r("password"),image:r("image"),submit:o("submit"),reset:o("reset"),button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},input:function(e){return it.test(e.nodeName)},focus:function(e){var t=e.ownerDocument;return e===t.activeElement&&(!t.hasFocus||t.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},active:function(e){return e===e.ownerDocument.activeElement},first:i(function(){return[0]}),last:i(function(e,t){return[t-1]}),eq:i(function(e,t,n){return[0>n?n+t:n]}),even:i(function(e,t){for(var n=0;t>n;n+=2)e.push(n);return e}),odd:i(function(e,t){for(var n=1;t>n;n+=2)e.push(n);return e}),lt:i(function(e,t,n){for(var r=0>n?n+t:n;--r>=0;)e.push(r);return e}),gt:i(function(e,t,n){for(var r=0>n?n+t:n;t>++r;)e.push(r);return e})}},k=R.compareDocumentPosition?function(e,t){return e===t?(A=!0,0):(e.compareDocumentPosition&&t.compareDocumentPosition?4&e.compareDocumentPosition(t):e.compareDocumentPosition)?-1:1}:function(e,t){if(e===t)return A=!0,0;if(e.sourceIndex&&t.sourceIndex)return e.sourceIndex-t.sourceIndex;var n,r,o=[],i=[],s=e.parentNode,l=t.parentNode,c=s;if(s===l)return a(e,t);if(!s)return-1;if(!l)return 1;for(;c;)o.unshift(c),c=c.parentNode;for(c=l;c;)i.unshift(c),c=c.parentNode;n=o.length,r=i.length;for(var u=0;n>u&&r>u;u++)if(o[u]!==i[u])return a(o[u],i[u]);return u===n?a(e,i[u],-1):a(o[u],t,1)},[0,0].sort(k),E=!A,n.uniqueSort=function(e){var t,n=[],r=1,o=0;if(A=E,e.sort(k),A){for(;t=e[r];r++)t===e[r-1]&&(o=n.push(r));for(;o--;)e.splice(n[o],1)}return e},n.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},C=n.compile=function(e,t){var n,r=[],o=[],i=W[N][e+" "];if(!i){for(t||(t=s(e)),n=t.length;n--;)i=f(t[n]),i[N]?r.push(i):o.push(i);i=W(e,d(o,r))}return i},j.querySelectorAll&&function(){var e,t=g,r=/'|\\/g,o=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,i=[":focus"],a=[":active"],l=R.matchesSelector||R.mozMatchesSelector||R.webkitMatchesSelector||R.oMatchesSelector||R.msMatchesSelector;lt(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||i.push("\\["+U+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||i.push(":checked")}),lt(function(e){e.innerHTML="

    ",e.querySelectorAll("[test^='']").length&&i.push("[*^$]="+U+"*(?:\"\"|'')"),e.innerHTML="",e.querySelectorAll(":enabled").length||i.push(":enabled",":disabled")}),i=RegExp(i.join("|")),g=function(e,n,o,a,l){if(!a&&!l&&!i.test(e)){var c,u,p=!0,f=N,d=n,h=9===n.nodeType&&e;if(1===n.nodeType&&"object"!==n.nodeName.toLowerCase()){for(c=s(e),(p=n.getAttribute("id"))?f=p.replace(r,"\\$&"):n.setAttribute("id",f),f="[id='"+f+"'] ",u=c.length;u--;)c[u]=f+c[u].join("");d=rt.test(e)&&n.parentNode||n,h=c.join(",")}if(h)try{return I.apply(o,P.call(d.querySelectorAll(h),0)),o}catch(g){}finally{p||n.removeAttribute("id")}}return t(e,n,o,a,l)},l&&(lt(function(t){e=l.call(t,"div");try{l.call(t,"[test!='']:sizzle"),a.push("!=",V)}catch(n){}}),a=RegExp(a.join("|")),n.matchesSelector=function(t,r){if(r=r.replace(o,"='$1']"),!w(t)&&!a.test(r)&&!i.test(r))try{var s=l.call(t,r);if(s||e||t.document&&11!==t.document.nodeType)return s}catch(c){}return n(r,null,null,[t]).length>0})}(),b.pseudos.nth=b.pseudos.eq,b.filters=m.prototype=b.pseudos,b.setFilters=new m,n.attr=Y.attr,Y.find=n,Y.expr=n.selectors,Y.expr[":"]=Y.expr.pseudos,Y.unique=n.uniqueSort,Y.text=n.getText,Y.isXMLDoc=n.isXML,Y.contains=n.contains}(e);var Mt=/Until$/,Ht=/^(?:parents|prev(?:Until|All))/,It=/^.[^:#\[\.,]*$/,Pt=Y.expr.match.needsContext,Dt={children:!0,contents:!0,next:!0,prev:!0};Y.fn.extend({find:function(e){var t,n,r,o,i,a,s=this;if("string"!=typeof e)return Y(e).filter(function(){for(t=0,n=s.length;n>t;t++)if(Y.contains(s[t],this))return!0});for(a=this.pushStack("","find",e),t=0,n=this.length;n>t;t++)if(r=a.length,Y.find(e,this[t],a),t>0)for(o=r;a.length>o;o++)for(i=0;r>i;i++)if(a[i]===a[o]){a.splice(o--,1);break}return a},has:function(e){var t,n=Y(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(Y.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(c(this,e,!1),"not",e)},filter:function(e){return this.pushStack(c(this,e,!0),"filter",e)},is:function(e){return!!e&&("string"==typeof e?Pt.test(e)?Y(e,this.context).index(this[0])>=0:Y.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){for(var n,r=0,o=this.length,i=[],a=Pt.test(e)||"string"!=typeof e?Y(e,t||this.context):0;o>r;r++)for(n=this[r];n&&n.ownerDocument&&n!==t&&11!==n.nodeType;){if(a?a.index(n)>-1:Y.find.matchesSelector(n,e)){i.push(n);break}n=n.parentNode}return i=i.length>1?Y.unique(i):i,this.pushStack(i,"closest",e)},index:function(e){return e?"string"==typeof e?Y.inArray(this[0],Y(e)):Y.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(e,t){var n="string"==typeof e?Y(e,t):Y.makeArray(e&&e.nodeType?[e]:e),r=Y.merge(this.get(),n);return this.pushStack(s(n[0])||s(r[0])?r:Y.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),Y.fn.andSelf=Y.fn.addBack,Y.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return Y.dir(e,"parentNode")},parentsUntil:function(e,t,n){return Y.dir(e,"parentNode",n)},next:function(e){return l(e,"nextSibling")},prev:function(e){return l(e,"previousSibling")},nextAll:function(e){return Y.dir(e,"nextSibling")},prevAll:function(e){return Y.dir(e,"previousSibling")},nextUntil:function(e,t,n){return Y.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return Y.dir(e,"previousSibling",n)},siblings:function(e){return Y.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return Y.sibling(e.firstChild)},contents:function(e){return Y.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:Y.merge([],e.childNodes)}},function(e,t){Y.fn[e]=function(n,r){var o=Y.map(this,t,n);return Mt.test(e)||(r=n),r&&"string"==typeof r&&(o=Y.filter(r,o)),o=this.length>1&&!Dt[e]?Y.unique(o):o,this.length>1&&Ht.test(e)&&(o=o.reverse()),this.pushStack(o,e,X.call(arguments).join(","))}}),Y.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),1===t.length?Y.find.matchesSelector(t[0],e)?[t[0]]:[]:Y.find.matches(e,t)},dir:function(e,n,r){for(var o=[],i=e[n];i&&9!==i.nodeType&&(r===t||1!==i.nodeType||!Y(i).is(r));)1===i.nodeType&&o.push(i),i=i[n];return o},sibling:function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});var Lt="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",Bt=/ jQuery\d+="(?:null|\d+)"/g,$t=/^\s+/,qt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,Wt=/<([\w:]+)/,Ut=/]","i"),Vt=/^(?:checkbox|radio)$/,Kt=/checked\s*(?:[^=]|=\s*.checked.)/i,Gt=/\/(java|ecma)script/i,Yt=/^\s*\s*$/g,Zt={option:[1,""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]},en=u($),tn=en.appendChild($.createElement("div"));Zt.optgroup=Zt.option,Zt.tbody=Zt.tfoot=Zt.colgroup=Zt.caption=Zt.thead,Zt.th=Zt.td,Y.support.htmlSerialize||(Zt._default=[1,"X
    ","
    "]),Y.fn.extend({text:function(e){return Y.access(this,function(e){return e===t?Y.text(this):this.empty().append((this[0]&&this[0].ownerDocument||$).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(Y.isFunction(e))return this.each(function(t){Y(this).wrapAll(e.call(this,t))});if(this[0]){var t=Y(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstChild&&1===e.firstChild.nodeType;)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return Y.isFunction(e)?this.each(function(t){Y(this).wrapInner(e.call(this,t))}):this.each(function(){var t=Y(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=Y.isFunction(e);return this.each(function(n){Y(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){Y.nodeName(this,"body")||Y(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType)&&this.insertBefore(e,this.firstChild)})},before:function(){if(!s(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this)});if(arguments.length){var e=Y.clean(arguments);return this.pushStack(Y.merge(e,this),"before",this.selector)}},after:function(){if(!s(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this.nextSibling)});if(arguments.length){var e=Y.clean(arguments);return this.pushStack(Y.merge(this,e),"after",this.selector)}},remove:function(e,t){for(var n,r=0;null!=(n=this[r]);r++)(!e||Y.filter(e,[n]).length)&&(t||1!==n.nodeType||(Y.cleanData(n.getElementsByTagName("*")),Y.cleanData([n])),n.parentNode&&n.parentNode.removeChild(n));return this},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)for(1===e.nodeType&&Y.cleanData(e.getElementsByTagName("*"));e.firstChild;)e.removeChild(e.firstChild);return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return Y.clone(this,e,t)})},html:function(e){return Y.access(this,function(e){var n=this[0]||{},r=0,o=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(Bt,""):t;if(!("string"!=typeof e||zt.test(e)||!Y.support.htmlSerialize&&Jt.test(e)||!Y.support.leadingWhitespace&&$t.test(e)||Zt[(Wt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(qt,"<$1>");try{for(;o>r;r++)n=this[r]||{},1===n.nodeType&&(Y.cleanData(n.getElementsByTagName("*")),n.innerHTML=e);n=0}catch(i){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){return s(this[0])?this.length?this.pushStack(Y(Y.isFunction(e)?e():e),"replaceWith",e):this:Y.isFunction(e)?this.each(function(t){var n=Y(this),r=n.html();n.replaceWith(e.call(this,t,r))}):("string"!=typeof e&&(e=Y(e).detach()),this.each(function(){var t=this.nextSibling,n=this.parentNode;Y(this).remove(),t?Y(t).before(e):Y(n).append(e)}))},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=[].concat.apply([],e);var o,i,a,s,l=0,c=e[0],u=[],f=this.length;if(!Y.support.checkClone&&f>1&&"string"==typeof c&&Kt.test(c))return this.each(function(){Y(this).domManip(e,n,r)});if(Y.isFunction(c))return this.each(function(o){var i=Y(this);e[0]=c.call(this,o,n?i.html():t),i.domManip(e,n,r)});if(this[0]){if(o=Y.buildFragment(e,this,u),a=o.fragment,i=a.firstChild,1===a.childNodes.length&&(a=i),i)for(n=n&&Y.nodeName(i,"tr"),s=o.cacheable||f-1;f>l;l++)r.call(n&&Y.nodeName(this[l],"table")?p(this[l],"tbody"):this[l],l===s?a:Y.clone(a,!0,!0));a=i=null,u.length&&Y.each(u,function(e,t){t.src?Y.ajax?Y.ajax({url:t.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):Y.error("no ajax"):Y.globalEval((t.text||t.textContent||t.innerHTML||"").replace(Yt,"")),t.parentNode&&t.parentNode.removeChild(t)})}return this}}),Y.buildFragment=function(e,n,r){var o,i,a,s=e[0];return n=n||$,n=!n.nodeType&&n[0]||n,n=n.ownerDocument||n,!(1===e.length&&"string"==typeof s&&512>s.length&&n===$&&"<"===s.charAt(0))||Xt.test(s)||!Y.support.checkClone&&Kt.test(s)||!Y.support.html5Clone&&Jt.test(s)||(i=!0,o=Y.fragments[s],a=o!==t),o||(o=n.createDocumentFragment(),Y.clean(e,n,o,r),i&&(Y.fragments[s]=a&&o)),{fragment:o,cacheable:i}},Y.fragments={},Y.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){Y.fn[e]=function(n){var r,o=0,i=[],a=Y(n),s=a.length,l=1===this.length&&this[0].parentNode;if((null==l||l&&11===l.nodeType&&1===l.childNodes.length)&&1===s)return a[t](this[0]),this;for(;s>o;o++)r=(o>0?this.clone(!0):this).get(),Y(a[o])[t](r),i=i.concat(r);return this.pushStack(i,e,a.selector)}}),Y.extend({clone:function(e,t,n){var r,o,i,a;if(Y.support.html5Clone||Y.isXMLDoc(e)||!Jt.test("<"+e.nodeName+">")?a=e.cloneNode(!0):(tn.innerHTML=e.outerHTML,tn.removeChild(a=tn.firstChild)),!(Y.support.noCloneEvent&&Y.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||Y.isXMLDoc(e)))for(d(e,a),r=h(e),o=h(a),i=0;r[i];++i)o[i]&&d(r[i],o[i]);if(t&&(f(e,a),n))for(r=h(e),o=h(a),i=0;r[i];++i)f(r[i],o[i]);return r=o=null,a},clean:function(e,n,r,o){var i,a,s,l,c,p,f,d,h,m,y,v=n===$&&en,b=[];for(n&&n.createDocumentFragment!==t||(n=$),i=0;null!=(s=e[i]);i++)if("number"==typeof s&&(s+=""),s){if("string"==typeof s)if(Qt.test(s)){for(v=v||u(n),f=n.createElement("div"),v.appendChild(f),s=s.replace(qt,"<$1>"),l=(Wt.exec(s)||["",""])[1].toLowerCase(),c=Zt[l]||Zt._default,p=c[0],f.innerHTML=c[1]+s+c[2];p--;)f=f.lastChild;if(!Y.support.tbody)for(d=Ut.test(s),h="table"!==l||d?""!==c[1]||d?[]:f.childNodes:f.firstChild&&f.firstChild.childNodes,a=h.length-1;a>=0;--a)Y.nodeName(h[a],"tbody")&&!h[a].childNodes.length&&h[a].parentNode.removeChild(h[a]);!Y.support.leadingWhitespace&&$t.test(s)&&f.insertBefore(n.createTextNode($t.exec(s)[0]),f.firstChild),s=f.childNodes,f.parentNode.removeChild(f)}else s=n.createTextNode(s);s.nodeType?b.push(s):Y.merge(b,s)}if(f&&(s=f=v=null),!Y.support.appendChecked)for(i=0;null!=(s=b[i]);i++)Y.nodeName(s,"input")?g(s):s.getElementsByTagName!==t&&Y.grep(s.getElementsByTagName("input"),g);if(r)for(m=function(e){return!e.type||Gt.test(e.type)?o?o.push(e.parentNode?e.parentNode.removeChild(e):e):r.appendChild(e):t},i=0;null!=(s=b[i]);i++)Y.nodeName(s,"script")&&m(s)||(r.appendChild(s),s.getElementsByTagName!==t&&(y=Y.grep(Y.merge([],s.getElementsByTagName("script")),m),b.splice.apply(b,[i+1,0].concat(y)),i+=y.length));return b},cleanData:function(e,t){for(var n,r,o,i,a=0,s=Y.expando,l=Y.cache,c=Y.support.deleteExpando,u=Y.event.special;null!=(o=e[a]);a++)if((t||Y.acceptData(o))&&(r=o[s],n=r&&l[r])){if(n.events)for(i in n.events)u[i]?Y.event.remove(o,i):Y.removeEvent(o,i,n.handle);l[r]&&(delete l[r],c?delete o[s]:o.removeAttribute?o.removeAttribute(s):o[s]=null,Y.deletedIds.push(r))}}}),function(){var e,t;Y.uaMatch=function(e){e=e.toLowerCase();var t=/(chrome)[ \/]([\w.]+)/.exec(e)||/(webkit)[ \/]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||0>e.indexOf("compatible")&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[];return{browser:t[1]||"",version:t[2]||"0"}},e=Y.uaMatch(W.userAgent),t={},e.browser&&(t[e.browser]=!0,t.version=e.version),t.chrome?t.webkit=!0:t.webkit&&(t.safari=!0),Y.browser=t,Y.sub=function(){function e(t,n){return new e.fn.init(t,n)}Y.extend(!0,e,this),e.superclass=this,e.fn=e.prototype=this(),e.fn.constructor=e,e.sub=this.sub,e.fn.init=function(n,r){return r&&r instanceof Y&&!(r instanceof e)&&(r=e(r)),Y.fn.init.call(this,n,r,t)},e.fn.init.prototype=e.fn;var t=e($);return e}}();var nn,rn,on,an=/alpha\([^)]*\)/i,sn=/opacity=([^)]*)/,ln=/^(top|right|bottom|left)$/,cn=/^(none|table(?!-c[ea]).+)/,un=/^margin/,pn=RegExp("^("+Z+")(.*)$","i"),fn=RegExp("^("+Z+")(?!px)[a-z%]+$","i"),dn=RegExp("^([-+])=("+Z+")","i"),hn={BODY:"block"},gn={position:"absolute",visibility:"hidden",display:"block"},mn={letterSpacing:0,fontWeight:400},yn=["Top","Right","Bottom","Left"],vn=["Webkit","O","Moz","ms"],bn=Y.fn.toggle;Y.fn.extend({css:function(e,n){return Y.access(this,function(e,n,r){return r!==t?Y.style(e,n,r):Y.css(e,n)},e,n,arguments.length>1)},show:function(){return v(this,!0)},hide:function(){return v(this)},toggle:function(e,t){var n="boolean"==typeof e;return Y.isFunction(e)&&Y.isFunction(t)?bn.apply(this,arguments):this.each(function(){(n?e:y(this))?Y(this).show():Y(this).hide()})}}),Y.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=nn(e,"opacity");return""===n?"1":n}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":Y.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,o){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,a,s,l=Y.camelCase(n),c=e.style;if(n=Y.cssProps[l]||(Y.cssProps[l]=m(c,l)),s=Y.cssHooks[n]||Y.cssHooks[l],r===t)return s&&"get"in s&&(i=s.get(e,!1,o))!==t?i:c[n];if(a=typeof r,"string"===a&&(i=dn.exec(r))&&(r=(i[1]+1)*i[2]+parseFloat(Y.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||Y.cssNumber[l]||(r+="px"),s&&"set"in s&&(r=s.set(e,r,o))===t)))try{c[n]=r}catch(u){}}},css:function(e,n,r,o){var i,a,s,l=Y.camelCase(n);return n=Y.cssProps[l]||(Y.cssProps[l]=m(e.style,l)),s=Y.cssHooks[n]||Y.cssHooks[l],s&&"get"in s&&(i=s.get(e,!0,o)),i===t&&(i=nn(e,n)),"normal"===i&&n in mn&&(i=mn[n]),r||o!==t?(a=parseFloat(i),r||Y.isNumeric(a)?a||0:i):i},swap:function(e,t,n){var r,o,i={};for(o in t)i[o]=e.style[o],e.style[o]=t[o];r=n.call(e);for(o in t)e.style[o]=i[o];return r}}),e.getComputedStyle?nn=function(t,n){var r,o,i,a,s=e.getComputedStyle(t,null),l=t.style;return s&&(r=s.getPropertyValue(n)||s[n],""!==r||Y.contains(t.ownerDocument,t)||(r=Y.style(t,n)),fn.test(r)&&un.test(n)&&(o=l.width,i=l.minWidth,a=l.maxWidth,l.minWidth=l.maxWidth=l.width=r,r=s.width,l.width=o,l.minWidth=i,l.maxWidth=a)),r}:$.documentElement.currentStyle&&(nn=function(e,t){var n,r,o=e.currentStyle&&e.currentStyle[t],i=e.style;return null==o&&i&&i[t]&&(o=i[t]),fn.test(o)&&!ln.test(t)&&(n=i.left,r=e.runtimeStyle&&e.runtimeStyle.left,r&&(e.runtimeStyle.left=e.currentStyle.left),i.left="fontSize"===t?"1em":o,o=i.pixelLeft+"px",i.left=n,r&&(e.runtimeStyle.left=r)),""===o?"auto":o}),Y.each(["height","width"],function(e,n){Y.cssHooks[n]={get:function(e,r,o){return r?0===e.offsetWidth&&cn.test(nn(e,"display"))?Y.swap(e,gn,function(){return w(e,n,o)}):w(e,n,o):t},set:function(e,t,r){return b(e,t,r?x(e,n,r,Y.support.boxSizing&&"border-box"===Y.css(e,"boxSizing")):0)}}}),Y.support.opacity||(Y.cssHooks.opacity={get:function(e,t){return sn.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,o=Y.isNumeric(t)?"alpha(opacity="+100*t+")":"",i=r&&r.filter||n.filter||"";n.zoom=1,t>=1&&""===Y.trim(i.replace(an,""))&&n.removeAttribute&&(n.removeAttribute("filter"),r&&!r.filter)||(n.filter=an.test(i)?i.replace(an,o):i+" "+o)}}),Y(function(){Y.support.reliableMarginRight||(Y.cssHooks.marginRight={get:function(e,n){return Y.swap(e,{display:"inline-block"},function(){return n?nn(e,"marginRight"):t})}}),!Y.support.pixelPosition&&Y.fn.position&&Y.each(["top","left"],function(e,t){Y.cssHooks[t]={get:function(e,n){if(n){var r=nn(e,t);return fn.test(r)?Y(e).position()[t]+"px":r}}}})}),Y.expr&&Y.expr.filters&&(Y.expr.filters.hidden=function(e){return 0===e.offsetWidth&&0===e.offsetHeight||!Y.support.reliableHiddenOffsets&&"none"===(e.style&&e.style.display||nn(e,"display"))},Y.expr.filters.visible=function(e){return!Y.expr.filters.hidden(e)}),Y.each({margin:"",padding:"",border:"Width"},function(e,t){Y.cssHooks[e+t]={expand:function(n){var r,o="string"==typeof n?n.split(" "):[n],i={};for(r=0;4>r;r++)i[e+yn[r]+t]=o[r]||o[r-2]||o[0];return i}},un.test(e)||(Y.cssHooks[e+t].set=b)});var xn=/%20/g,wn=/\[\]$/,_n=/\r?\n/g,Cn=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,kn=/^(?:select|textarea)/i; +Y.fn.extend({serialize:function(){return Y.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?Y.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||kn.test(this.nodeName)||Cn.test(this.type))}).map(function(e,t){var n=Y(this).val();return null==n?null:Y.isArray(n)?Y.map(n,function(e){return{name:t.name,value:e.replace(_n,"\r\n")}}):{name:t.name,value:n.replace(_n,"\r\n")}}).get()}}),Y.param=function(e,n){var r,o=[],i=function(e,t){t=Y.isFunction(t)?t():null==t?"":t,o[o.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(n===t&&(n=Y.ajaxSettings&&Y.ajaxSettings.traditional),Y.isArray(e)||e.jquery&&!Y.isPlainObject(e))Y.each(e,function(){i(this.name,this.value)});else for(r in e)C(r,e[r],n,i);return o.join("&").replace(xn,"+")};var An,Tn,En=/#.*$/,Fn=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Nn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,Sn=/^(?:GET|HEAD)$/,jn=/^\/\//,Rn=/\?/,On=/)<[^<]*)*<\/script>/gi,Mn=/([?&])_=[^&]*/,Hn=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,In=Y.fn.load,Pn={},Dn={},Ln=["*/"]+["*"];try{Tn=q.href}catch(Bn){Tn=$.createElement("a"),Tn.href="",Tn=Tn.href}An=Hn.exec(Tn.toLowerCase())||[],Y.fn.load=function(e,n,r){if("string"!=typeof e&&In)return In.apply(this,arguments);if(!this.length)return this;var o,i,a,s=this,l=e.indexOf(" ");return l>=0&&(o=e.slice(l,e.length),e=e.slice(0,l)),Y.isFunction(n)?(r=n,n=t):n&&"object"==typeof n&&(i="POST"),Y.ajax({url:e,type:i,dataType:"html",data:n,complete:function(e,t){r&&s.each(r,a||[e.responseText,t,e])}}).done(function(e){a=arguments,s.html(o?Y("
    ").append(e.replace(On,"")).find(o):e)}),this},Y.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(e,t){Y.fn[t]=function(e){return this.on(t,e)}}),Y.each(["get","post"],function(e,n){Y[n]=function(e,r,o,i){return Y.isFunction(r)&&(i=i||o,o=r,r=t),Y.ajax({type:n,url:e,data:r,success:o,dataType:i})}}),Y.extend({getScript:function(e,n){return Y.get(e,t,n,"script")},getJSON:function(e,t,n){return Y.get(e,t,n,"json")},ajaxSetup:function(e,t){return t?T(e,Y.ajaxSettings):(t=e,e=Y.ajaxSettings),T(e,t),e},ajaxSettings:{url:Tn,isLocal:Nn.test(An[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":Ln},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":Y.parseJSON,"text xml":Y.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:k(Pn),ajaxTransport:k(Dn),ajax:function(e,n){function r(e,n,r,a){var c,p,v,b,w,C=n;2!==x&&(x=2,l&&clearTimeout(l),s=t,i=a||"",_.readyState=e>0?4:0,r&&(b=E(f,_,r)),e>=200&&300>e||304===e?(f.ifModified&&(w=_.getResponseHeader("Last-Modified"),w&&(Y.lastModified[o]=w),w=_.getResponseHeader("Etag"),w&&(Y.etag[o]=w)),304===e?(C="notmodified",c=!0):(c=F(f,b),C=c.state,p=c.data,v=c.error,c=!v)):(v=C,(!C||e)&&(C="error",0>e&&(e=0))),_.status=e,_.statusText=(n||C)+"",c?g.resolveWith(d,[p,C,_]):g.rejectWith(d,[_,C,v]),_.statusCode(y),y=t,u&&h.trigger("ajax"+(c?"Success":"Error"),[_,f,c?p:v]),m.fireWith(d,[_,C]),u&&(h.trigger("ajaxComplete",[_,f]),--Y.active||Y.event.trigger("ajaxStop")))}"object"==typeof e&&(n=e,e=t),n=n||{};var o,i,a,s,l,c,u,p,f=Y.ajaxSetup({},n),d=f.context||f,h=d!==f&&(d.nodeType||d instanceof Y)?Y(d):Y.event,g=Y.Deferred(),m=Y.Callbacks("once memory"),y=f.statusCode||{},v={},b={},x=0,w="canceled",_={readyState:0,setRequestHeader:function(e,t){if(!x){var n=e.toLowerCase();e=b[n]=b[n]||e,v[e]=t}return this},getAllResponseHeaders:function(){return 2===x?i:null},getResponseHeader:function(e){var n;if(2===x){if(!a)for(a={};n=Fn.exec(i);)a[n[1].toLowerCase()]=n[2];n=a[e.toLowerCase()]}return n===t?null:n},overrideMimeType:function(e){return x||(f.mimeType=e),this},abort:function(e){return e=e||w,s&&s.abort(e),r(0,e),this}};if(g.promise(_),_.success=_.done,_.error=_.fail,_.complete=m.add,_.statusCode=function(e){if(e){var t;if(2>x)for(t in e)y[t]=[y[t],e[t]];else t=e[_.status],_.always(t)}return this},f.url=((e||f.url)+"").replace(En,"").replace(jn,An[1]+"//"),f.dataTypes=Y.trim(f.dataType||"*").toLowerCase().split(tt),null==f.crossDomain&&(c=Hn.exec(f.url.toLowerCase()),f.crossDomain=!(!c||c[1]===An[1]&&c[2]===An[2]&&(c[3]||("http:"===c[1]?80:443))==(An[3]||("http:"===An[1]?80:443)))),f.data&&f.processData&&"string"!=typeof f.data&&(f.data=Y.param(f.data,f.traditional)),A(Pn,f,n,_),2===x)return _;if(u=f.global,f.type=f.type.toUpperCase(),f.hasContent=!Sn.test(f.type),u&&0===Y.active++&&Y.event.trigger("ajaxStart"),!f.hasContent&&(f.data&&(f.url+=(Rn.test(f.url)?"&":"?")+f.data,delete f.data),o=f.url,f.cache===!1)){var C=Y.now(),k=f.url.replace(Mn,"$1_="+C);f.url=k+(k===f.url?(Rn.test(f.url)?"&":"?")+"_="+C:"")}(f.data&&f.hasContent&&f.contentType!==!1||n.contentType)&&_.setRequestHeader("Content-Type",f.contentType),f.ifModified&&(o=o||f.url,Y.lastModified[o]&&_.setRequestHeader("If-Modified-Since",Y.lastModified[o]),Y.etag[o]&&_.setRequestHeader("If-None-Match",Y.etag[o])),_.setRequestHeader("Accept",f.dataTypes[0]&&f.accepts[f.dataTypes[0]]?f.accepts[f.dataTypes[0]]+("*"!==f.dataTypes[0]?", "+Ln+"; q=0.01":""):f.accepts["*"]);for(p in f.headers)_.setRequestHeader(p,f.headers[p]);if(f.beforeSend&&(f.beforeSend.call(d,_,f)===!1||2===x))return _.abort();w="abort";for(p in{success:1,error:1,complete:1})_[p](f[p]);if(s=A(Dn,f,n,_)){_.readyState=1,u&&h.trigger("ajaxSend",[_,f]),f.async&&f.timeout>0&&(l=setTimeout(function(){_.abort("timeout")},f.timeout));try{x=1,s.send(v,r)}catch(T){if(!(2>x))throw T;r(-1,T)}}else r(-1,"No Transport");return _},active:0,lastModified:{},etag:{}});var $n=[],qn=/\?/,Wn=/(=)\?(?=&|$)|\?\?/,Un=Y.now();Y.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=$n.pop()||Y.expando+"_"+Un++;return this[e]=!0,e}}),Y.ajaxPrefilter("json jsonp",function(n,r,o){var i,a,s,l=n.data,c=n.url,u=n.jsonp!==!1,p=u&&Wn.test(c),f=u&&!p&&"string"==typeof l&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Wn.test(l);return"jsonp"===n.dataTypes[0]||p||f?(i=n.jsonpCallback=Y.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,a=e[i],p?n.url=c.replace(Wn,"$1"+i):f?n.data=l.replace(Wn,"$1"+i):u&&(n.url+=(qn.test(c)?"&":"?")+n.jsonp+"="+i),n.converters["script json"]=function(){return s||Y.error(i+" was not called"),s[0]},n.dataTypes[0]="json",e[i]=function(){s=arguments},o.always(function(){e[i]=a,n[i]&&(n.jsonpCallback=r.jsonpCallback,$n.push(i)),s&&Y.isFunction(a)&&a(s[0]),s=a=t}),"script"):t}),Y.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(e){return Y.globalEval(e),e}}}),Y.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),Y.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=$.head||$.getElementsByTagName("head")[0]||$.documentElement;return{send:function(o,i){n=$.createElement("script"),n.async="async",e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,o){(o||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,r&&n.parentNode&&r.removeChild(n),n=t,o||i(200,"success"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(0,1)}}}});var Qn,zn=e.ActiveXObject?function(){for(var e in Qn)Qn[e](0,1)}:!1,Xn=0;Y.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&N()||S()}:N,function(e){Y.extend(Y.support,{ajax:!!e,cors:!!e&&"withCredentials"in e})}(Y.ajaxSettings.xhr()),Y.support.ajax&&Y.ajaxTransport(function(n){if(!n.crossDomain||Y.support.cors){var r;return{send:function(o,i){var a,s,l=n.xhr();if(n.username?l.open(n.type,n.url,n.async,n.username,n.password):l.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)l[s]=n.xhrFields[s];n.mimeType&&l.overrideMimeType&&l.overrideMimeType(n.mimeType),n.crossDomain||o["X-Requested-With"]||(o["X-Requested-With"]="XMLHttpRequest");try{for(s in o)l.setRequestHeader(s,o[s])}catch(c){}l.send(n.hasContent&&n.data||null),r=function(e,o){var s,c,u,p,f;try{if(r&&(o||4===l.readyState))if(r=t,a&&(l.onreadystatechange=Y.noop,zn&&delete Qn[a]),o)4!==l.readyState&&l.abort();else{s=l.status,u=l.getAllResponseHeaders(),p={},f=l.responseXML,f&&f.documentElement&&(p.xml=f);try{p.text=l.responseText}catch(d){}try{c=l.statusText}catch(d){c=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=p.text?200:404}}catch(h){o||i(-1,h)}p&&i(s,c,p,u)},n.async?4===l.readyState?setTimeout(r,0):(a=++Xn,zn&&(Qn||(Qn={},Y(e).unload(zn)),Qn[a]=r),l.onreadystatechange=r):r()},abort:function(){r&&r(0,1)}}}});var Jn,Vn,Kn=/^(?:toggle|show|hide)$/,Gn=RegExp("^(?:([-+])=|)("+Z+")([a-z%]*)$","i"),Yn=/queueHooks$/,Zn=[H],er={"*":[function(e,t){var n,r,o=this.createTween(e,t),i=Gn.exec(t),a=o.cur(),s=+a||0,l=1,c=20;if(i){if(n=+i[2],r=i[3]||(Y.cssNumber[e]?"":"px"),"px"!==r&&s){s=Y.css(o.elem,e,!0)||n||1;do l=l||".5",s/=l,Y.style(o.elem,e,s+r);while(l!==(l=o.cur()/a)&&1!==l&&--c)}o.unit=r,o.start=s,o.end=i[1]?s+(i[1]+1)*n:n}return o}]};Y.Animation=Y.extend(O,{tweener:function(e,t){Y.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");for(var n,r=0,o=e.length;o>r;r++)n=e[r],er[n]=er[n]||[],er[n].unshift(t)},prefilter:function(e,t){t?Zn.unshift(e):Zn.push(e)}}),Y.Tween=I,I.prototype={constructor:I,init:function(e,t,n,r,o,i){this.elem=e,this.prop=n,this.easing=o||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=i||(Y.cssNumber[n]?"":"px")},cur:function(){var e=I.propHooks[this.prop];return e&&e.get?e.get(this):I.propHooks._default.get(this)},run:function(e){var t,n=I.propHooks[this.prop];return this.pos=t=this.options.duration?Y.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):I.propHooks._default.set(this),this}},I.prototype.init.prototype=I.prototype,I.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=Y.css(e.elem,e.prop,!1,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){Y.fx.step[e.prop]?Y.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[Y.cssProps[e.prop]]||Y.cssHooks[e.prop])?Y.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},I.propHooks.scrollTop=I.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},Y.each(["toggle","show","hide"],function(e,t){var n=Y.fn[t];Y.fn[t]=function(r,o,i){return null==r||"boolean"==typeof r||!e&&Y.isFunction(r)&&Y.isFunction(o)?n.apply(this,arguments):this.animate(P(t,!0),r,o,i)}}),Y.fn.extend({fadeTo:function(e,t,n,r){return this.filter(y).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var o=Y.isEmptyObject(e),i=Y.speed(t,n,r),a=function(){var t=O(this,Y.extend({},e),i);o&&t.stop(!0)};return o||i.queue===!1?this.each(a):this.queue(i.queue,a)},stop:function(e,n,r){var o=function(e){var t=e.stop;delete e.stop,t(r)};return"string"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=null!=e&&e+"queueHooks",i=Y.timers,a=Y._data(this);if(n)a[n]&&a[n].stop&&o(a[n]);else for(n in a)a[n]&&a[n].stop&&Yn.test(n)&&o(a[n]);for(n=i.length;n--;)i[n].elem!==this||null!=e&&i[n].queue!==e||(i[n].anim.stop(r),t=!1,i.splice(n,1));(t||!r)&&Y.dequeue(this,e)})}}),Y.each({slideDown:P("show"),slideUp:P("hide"),slideToggle:P("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){Y.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),Y.speed=function(e,t,n){var r=e&&"object"==typeof e?Y.extend({},e):{complete:n||!n&&t||Y.isFunction(e)&&e,duration:e,easing:n&&t||t&&!Y.isFunction(t)&&t};return r.duration=Y.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in Y.fx.speeds?Y.fx.speeds[r.duration]:Y.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){Y.isFunction(r.old)&&r.old.call(this),r.queue&&Y.dequeue(this,r.queue)},r},Y.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},Y.timers=[],Y.fx=I.prototype.init,Y.fx.tick=function(){var e,n=Y.timers,r=0;for(Jn=Y.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||Y.fx.stop(),Jn=t},Y.fx.timer=function(e){e()&&Y.timers.push(e)&&!Vn&&(Vn=setInterval(Y.fx.tick,Y.fx.interval))},Y.fx.interval=13,Y.fx.stop=function(){clearInterval(Vn),Vn=null},Y.fx.speeds={slow:600,fast:200,_default:400},Y.fx.step={},Y.expr&&Y.expr.filters&&(Y.expr.filters.animated=function(e){return Y.grep(Y.timers,function(t){return e===t.elem}).length});var tr=/^(?:body|html)$/i;Y.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){Y.offset.setOffset(this,e,t)});var n,r,o,i,a,s,l,c={top:0,left:0},u=this[0],p=u&&u.ownerDocument;if(p)return(r=p.body)===u?Y.offset.bodyOffset(u):(n=p.documentElement,Y.contains(n,u)?(u.getBoundingClientRect!==t&&(c=u.getBoundingClientRect()),o=D(p),i=n.clientTop||r.clientTop||0,a=n.clientLeft||r.clientLeft||0,s=o.pageYOffset||n.scrollTop,l=o.pageXOffset||n.scrollLeft,{top:c.top+s-i,left:c.left+l-a}):c)},Y.offset={bodyOffset:function(e){var t=e.offsetTop,n=e.offsetLeft;return Y.support.doesNotIncludeMarginInBodyOffset&&(t+=parseFloat(Y.css(e,"marginTop"))||0,n+=parseFloat(Y.css(e,"marginLeft"))||0),{top:t,left:n}},setOffset:function(e,t,n){var r=Y.css(e,"position");"static"===r&&(e.style.position="relative");var o,i,a=Y(e),s=a.offset(),l=Y.css(e,"top"),c=Y.css(e,"left"),u=("absolute"===r||"fixed"===r)&&Y.inArray("auto",[l,c])>-1,p={},f={};u?(f=a.position(),o=f.top,i=f.left):(o=parseFloat(l)||0,i=parseFloat(c)||0),Y.isFunction(t)&&(t=t.call(e,n,s)),null!=t.top&&(p.top=t.top-s.top+o),null!=t.left&&(p.left=t.left-s.left+i),"using"in t?t.using.call(e,p):a.css(p)}},Y.fn.extend({position:function(){if(this[0]){var e=this[0],t=this.offsetParent(),n=this.offset(),r=tr.test(t[0].nodeName)?{top:0,left:0}:t.offset();return n.top-=parseFloat(Y.css(e,"marginTop"))||0,n.left-=parseFloat(Y.css(e,"marginLeft"))||0,r.top+=parseFloat(Y.css(t[0],"borderTopWidth"))||0,r.left+=parseFloat(Y.css(t[0],"borderLeftWidth"))||0,{top:n.top-r.top,left:n.left-r.left}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent||$.body;e&&!tr.test(e.nodeName)&&"static"===Y.css(e,"position");)e=e.offsetParent;return e||$.body})}}),Y.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);Y.fn[e]=function(o){return Y.access(this,function(e,o,i){var a=D(e);return i===t?a?n in a?a[n]:a.document.documentElement[o]:e[o]:(a?a.scrollTo(r?Y(a).scrollLeft():i,r?i:Y(a).scrollTop()):e[o]=i,t)},e,o,arguments.length,null)}}),Y.each({Height:"height",Width:"width"},function(e,n){Y.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,o){Y.fn[o]=function(o,i){var a=arguments.length&&(r||"boolean"!=typeof o),s=r||(o===!0||i===!0?"margin":"border");return Y.access(this,function(n,r,o){var i;return Y.isWindow(n)?n.document.documentElement["client"+e]:9===n.nodeType?(i=n.documentElement,Math.max(n.body["scroll"+e],i["scroll"+e],n.body["offset"+e],i["offset"+e],i["client"+e])):o===t?Y.css(n,r,o,s):Y.style(n,r,o,s)},n,a?o:t,a,null)}})}),e.jQuery=e.$=Y,"function"==typeof define&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return Y})}(window),/*! ========================================================= * bootstrap-modal.js v2.2.2 * http://twitter.github.com/bootstrap/javascript.html#modals * ========================================================= @@ -38,36 +47,7 @@ * See the License for the specific language governing permissions and * limitations under the License. * ========================================================= */ -;!function(e){"use strict";var t=function(t,n){this.options=n,this.$element=e(t).delegate('[data-dismiss="modal"]',"click.dismiss.modal",e.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};t.prototype={constructor:t,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var t=this,n=e.Event("show");this.$element.trigger(n);if(this.isShown||n.isDefaultPrevented())return;this.isShown=!0,this.escape(),this.backdrop(function(){var n=e.support.transition&&t.$element.hasClass("fade");t.$element.parent().length||t.$element.appendTo(document.body),t.$element.show(),n&&t.$element[0].offsetWidth,t.$element.addClass("in").attr("aria-hidden",!1),t.enforceFocus(),n?t.$element.one(e.support.transition.end,function(){t.$element.focus().trigger("shown")}):t.$element.focus().trigger("shown")})},hide:function(t){t&&t.preventDefault();var n=this;t=e.Event("hide"),this.$element.trigger(t);if(!this.isShown||t.isDefaultPrevented())return;this.isShown=!1,this.escape(),e(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),e.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal()},enforceFocus:function(){var t=this;e(document).on("focusin.modal",function(e){t.$element[0]!==e.target&&!t.$element.has(e.target).length&&t.$element.focus()})},escape:function(){var e=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(t){t.which==27&&e.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var t=this,n=setTimeout(function(){t.$element.off(e.support.transition.end),t.hideModal()},500);this.$element.one(e.support.transition.end,function(){clearTimeout(n),t.hideModal()})},hideModal:function(e){this.$element.hide().trigger("hidden"),this.backdrop()},removeBackdrop:function(){this.$backdrop.remove(),this.$backdrop=null},backdrop:function(t){var n=this,r=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var i=e.support.transition&&r;this.$backdrop=e('
    @@ -51,19 +51,19 @@ - + @@ -76,5 +76,5 @@ -
    -
    \ No newline at end of file +
    +
    diff --git a/docs/src/template/less/code.less b/docs/src/template/less/code.less index f1851a07..a78ae002 100755 --- a/docs/src/template/less/code.less +++ b/docs/src/template/less/code.less @@ -55,4 +55,10 @@ pre { .pre-scrollable { max-height: 340px; overflow-y: scroll; +} + +a { + code { + border-color: #e1e1e8 !important; + } } \ No newline at end of file diff --git a/docs/src/template/style.html b/docs/src/template/style.html index ed48ab0a..46137ab8 100644 --- a/docs/src/template/style.html +++ b/docs/src/template/style.html @@ -3,7 +3,7 @@ - + @@ -12,7 +12,7 @@ - + + + + + + + - + @@ -22,6 +41,9 @@ + + + - - - - - diff --git a/tests/index.html b/tests/index.html index ac4d825e..48fd3e4c 100644 --- a/tests/index.html +++ b/tests/index.html @@ -2,27 +2,48 @@ "http://www.w3.org/TR/html4/loose.dtd"> - Jasmine Spec Runner - + F2 - Jasmine Unit Tests + + + + + + + + + + + + + + + + + + - + - + + + - - - - - + + diff --git a/tests/js/initialize.js b/tests/js/initialize.js new file mode 100644 index 00000000..95fc18e6 --- /dev/null +++ b/tests/js/initialize.js @@ -0,0 +1,18 @@ +(function(window) { + + // set a variable so that the index page knows the server is up and running + window.F2_NODE_TEST_SERVER = {}; + + // pick apart the url to verify that we're running on the node server + var rurl = /^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/, + url = window.location.href.toLowerCase(), + parts = rurl.exec(url); + //console.log(parts); + + // run the tests on the node server if it is running so that the port numbers + // will match + if (parts[1] != 'http:' || parts[2] != 'localhost' || parts[3] != '8080') { + window.location = 'http://localhost:8080/tests' + /\/[\w.+-]+\.html$/.exec(window.location.pathname)[0]; + } + +})(window); \ No newline at end of file diff --git a/tests/js/preloadedTest-amd.js b/tests/js/preloadedTest-amd.js new file mode 100644 index 00000000..45d8ac82 --- /dev/null +++ b/tests/js/preloadedTest-amd.js @@ -0,0 +1,29 @@ +require(['../sdk/f2.min.js'], function () { + F2.Apps[TEST_APP_ID] = (function() { + + var App_Class = function (appConfig, appContent, root) { + this.appConfig = appConfig; + this.appContent = appContent; + this.ui = appConfig.ui; + this.$root = $(root); + this.doNotCallAgain = false; + }; + + App_Class.prototype.init = function () { + F2.PreloadAppInitializedCounter++; + var context = this; + F2.Events.on('PreloadAppCommuncation', function(){ if(!context.doNotCallAgain) { context._handleEmit(); } }); + F2.PreloadAppInitialized = true; + }; + + App_Class.prototype._handleEmit = function (bArg) { + F2.PreloadRetrievedEmit = true; + F2.PreloadRetrievedEmitCounter++; + F2.PreloadTestCompleteCounter++; + F2.PreloadTestComplete = true; + this.doNotCallAgain = true; + }; + + return App_Class; + })(); +}); \ No newline at end of file diff --git a/tests/js/preloadedTest.js b/tests/js/preloadedTest.js new file mode 100644 index 00000000..52eb0f94 --- /dev/null +++ b/tests/js/preloadedTest.js @@ -0,0 +1,27 @@ +F2.Apps[TEST_APP_ID] = (function() { + + var App_Class = function (appConfig, appContent, root) { + this.appConfig = appConfig; + this.appContent = appContent; + this.ui = appConfig.ui; + this.$root = $(root); + this.doNotCallAgain = false; + }; + + App_Class.prototype.init = function () { + F2.PreloadAppInitializedCounter++; + var context = this; + F2.Events.on("PreloadAppCommuncation", function(){ if(!context.doNotCallAgain) { context._handleEmit(); } }); + F2.PreloadAppInitialized = true; + }; + + App_Class.prototype._handleEmit = function (bArg) { + F2.PreloadRetrievedEmit = true; + F2.PreloadRetrievedEmitCounter++; + F2.PreloadTestCompleteCounter++; + F2.PreloadTestComplete = true; + this.doNotCallAgain = true; + }; + + return App_Class; +})(); \ No newline at end of file diff --git a/tests/js/test.js b/tests/js/test.js index e69de29b..9549ea75 100644 --- a/tests/js/test.js +++ b/tests/js/test.js @@ -0,0 +1,22 @@ +F2.Apps["com_openf2_tests_helloworld"] = (function() { + + F2.testAppInitialized = false; + + var App_Class = function (appConfig, appContent, root) { + this.appConfig = appConfig; + this.appContent = appContent; + this.ui = appConfig.ui; + }; + + App_Class.prototype.init = function () { + F2.destroyAppMethodCalled = false; + F2.testAppInstanceID = this.appConfig.instanceId; + F2.testAppInitialized = true; + }; + + App_Class.prototype.destroy = function () { + F2.destroyAppMethodCalled = true; + }; + + return App_Class; +})(); \ No newline at end of file diff --git a/tests/server.js b/tests/server.js new file mode 100644 index 00000000..fd0e98fb --- /dev/null +++ b/tests/server.js @@ -0,0 +1,31 @@ +var express = require('express'); +var app = express(); + +/** + * Checks whether a GET or a POST was used when making the request. This method + * will output an AppManifest with an AppClass that will log some data. + * See container-spec.js and search for httpPostTest + */ +app.use('/httpPostTest', function(req, res) { + res.setHeader('Content-Type', 'application/json'); + res.end('F2_jsonpCallback_com_test_app(' + JSON.stringify({ + inlineScripts: [ + [ + 'F2.Apps["com_test_app"] = function(appConfig, appContent, root) {', + 'F2.log(appContent.data.method == "POST");', + '};' + ].join('') + ], + apps: [ + { + data: { + method: req.method + }, + html: '
    ' + } + ] + }) + ')'); +}); + +// export the module for use with grunt +module.exports = app; \ No newline at end of file diff --git a/tests/spec/amd-spec.js b/tests/spec/amd-spec.js index c57cec9c..db80c33b 100644 --- a/tests/spec/amd-spec.js +++ b/tests/spec/amd-spec.js @@ -4,7 +4,7 @@ describe('AMD', function() { var isLoaded = false; - require(["../sdk/f2.debug.js"], function (nonGlobalF2) { + require(["../sdk/f2.min.js"], function (nonGlobalF2) { isLoaded = typeof nonGlobalF2 !== "undefined"; }); diff --git a/tests/spec/app-handlers-spec.js b/tests/spec/app-handlers-spec.js new file mode 100644 index 00000000..7186657e --- /dev/null +++ b/tests/spec/app-handlers-spec.js @@ -0,0 +1,2244 @@ +describe('F2.AppHandlers', function() { + + var containerAppHandlerToken = null; + + var async = new AsyncSpec(this); + async.beforeEachReloadF2(function() { if(F2.AppHandlers.getToken) { containerAppHandlerToken = F2.AppHandlers.getToken(); } }); + + var appConfig = function() + { + return { + appId: TEST_APP_ID, + manifestUrl: TEST_MANIFEST_URL + }; + }; + + var appManifest = function() + { + return { + scripts:[], + styles:[], + inlineScripts:[], + apps:[ + { + html: '
    Testing
    ' + } + ] + } + }; + + it( + + 'should fire beforeAppRender, appRender, or afterAppRender if they are defined in container config and not APP_RENDER_* AppHandler methods.', + function() { + var isBeforeAppRenderFired = false; + + F2.init({ + beforeAppRender: function() + { + isBeforeAppRenderFired = true; + } + }); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_BEFORE, + function(appConfig) + { + throw("I should not fire!"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER, + function(appConfig) + { + throw("I should not fire!"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + throw("I should not fire!"); + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return isBeforeAppRenderFired; + }, + 'beforeAppRender was never fired', + 3000 + ); + + runs(function() { expect(isBeforeAppRenderFired).toBeTruthy(); }); + } + ); + + it( + 'should fire app handlers if beforeAppRender, appRender, and afterAppRender are NOT defined in container config', + function() { + var isAppRenderBeforeFired = false; + + F2.init(); + + F2.AppHandlers.on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_BEFORE, + function(appConfig) + { + setTimeout(function(){ isAppRenderBeforeFired = true}, 100); + } + ).on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + setTimeout(function(){ isAppRenderBeforeFired = true}, 100); + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return isAppRenderBeforeFired; + }, + 'F2.AppHandlers.on("appRenderBefore") was never fired', + 3000 + ); + + runs(function() { expect(isAppRenderBeforeFired).toBeTruthy(); }); + } + ); + + it( + 'should not allow F2.AppHandlers.on() handler registration without valid token', + function() { + expect(function(){ + F2.init(); + + F2.AppHandlers.on( + "", + F2.Constants.AppHandlers.APP_RENDER_BEFORE, + function(appConfig) {} + ); + + F2.registerApps(appConfig(), appManifest()); + }).toThrow(); + } + ); + + it( + 'should not allow F2.AppHandlers.off() handler registration without valid token', + function() { + expect(function(){ + F2.init(); + + F2.AppHandlers.off( + "", + F2.Constants.AppHandlers.APP_RENDER_BEFORE + ); + + F2.registerApps(appConfig(), appManifest()); + }).toThrow(); + } + ); + + it( + 'F2.AppHandlers.getToken() method should be destroyed after first call.', + function() { + // F2.AppHandlers.getToken is called above in the async.beforeEachReloadF2 + expect(F2.AppHandlers.getToken).toBeFalsy(); + } + ); + + it( + 'F2.AppHandlers.__f2GetToken() method should be destroyed after first call.', + function() { + // F2.AppHandlers.__f2GetToken is called internally and should no longer exist + expect(F2.AppHandlers.__f2GetToken).toBeFalsy(); + } + ); + + it( + 'container should not be allowed to trigger F2.AppHandlers.on() events.', + function() { + expect(function(){ + F2.init(); + + F2.AppHandlers.__trigger( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_BEFORE + ); + }).toThrow(); + } + ); + + it( + 'F2.AppHandlers should not be required to load apps.', + function() { + expect(function(){ + F2.init(); + F2.registerApps(appConfig(), appManifest()); + }).not.toThrow(); + } + ); + + it( + 'render methods should fire sequentially appCreateRoot, appRenderBefore, appRender, and then appRenderAfter.', + function() { + var bDone = false; + var sOrder = null; + var arOrder = []; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_CREATE_ROOT, + function(appConfig) + { + appConfig.root = $("
    ").get(0); + arOrder.push(F2.Constants.AppHandlers.APP_CREATE_ROOT); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_BEFORE, + function(appConfig) + { + arOrder.push(F2.Constants.AppHandlers.APP_RENDER_BEFORE); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER, + function(appConfig) + { + $("body").append($(appConfig.root)); + arOrder.push(F2.Constants.AppHandlers.APP_RENDER); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + arOrder.push(F2.Constants.AppHandlers.APP_RENDER_AFTER); + sOrder = arOrder.join(","); + bDone = true; + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers were never fired', + 3000 + ); + + runs(function() { expect(sOrder).toBe("appCreateRoot,appRenderBefore,appRender,appRenderAfter"); }); + } + ); + +}); + +describe('F2.AppHandlers - rendering - appCreateRoot', function() { + + var containerAppHandlerToken = null; + + var async = new AsyncSpec(this); + async.beforeEachReloadF2(function() { if(F2.AppHandlers.getToken) { containerAppHandlerToken = F2.AppHandlers.getToken(); } }); + + var appConfig = function() + { + return { + appId: TEST_APP_ID, + manifestUrl: TEST_MANIFEST_URL + }; + }; + + var appManifest = function() + { + return { + scripts:[], + styles:[], + inlineScripts:[], + apps:[ + { + html: '
    Testing
    ' + } + ] + }; + }; + + it( + 'should create appRoot using the apps html if appCreateRoot event is not bound and render appRoot to the page automatically.', + function() { + var bDone = false; + var bRootOnPage = false; + var bAppHtmlInRoot = false; + var bHasTestAppClass = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + var $root = $(appConfig.root); + bRootOnPage = ($root.parents("body:first").length > 0); + bHasTestAppClass = $root.hasClass("test-app"); + bAppHtmlInRoot = ($root.text() == "Testing"); + bDone = true; + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect(bHasTestAppClass).toBe(true); + expect(bRootOnPage).toBe(true); + expect(bAppHtmlInRoot).toBe(true); + }); + } + ); + + it( + 'should pass appConfig as only argument to appCreateRoot.', + function() { + var bDone = false; + var bHasAppConfig = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_CREATE_ROOT, + function(appConfig) + { + appConfig.root = $("

    ").get(0); + bHasAppConfig = (arguments.length == 1 && appConfig && appConfig.appId && appConfig.manifestUrl) ? true : false; + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + bDone = true; + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect(bHasAppConfig).toBe(true); + }); + } + ); + + it( + 'respects appCreateRoot setting appConfig.root and appends app html by default.', + function() { + var bDone = false; + var bRootIsH1 = false; + var bRootOnPage = false; + var bAppHtmlInRoot = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_CREATE_ROOT, + function(appConfig) + { + appConfig.root = $("

    ").get(0); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + var $root = $(appConfig.root); + bRootIsH1 = $root.is("h1"); + bRootOnPage = ($root.parents("body:first").length > 0); + bAppHtmlInRoot = ($root.find("div.test-app").length > 0); + bDone = true; + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { expect(bRootIsH1).toBe(true); expect(bRootOnPage).toBe(true); expect(bAppHtmlInRoot).toBe(true); }); + } + ); + + it( + 'fires appCreateRoot functions sequentially.', + function() { + var bDone = false; + var arOrder = []; + var bRootIsApp = false; + var bRootOnPage = false; + var bAppHtmlInRoot = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_CREATE_ROOT, + function(appConfig) + { + appConfig.root = $("").get(0); + arOrder.push("1"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_CREATE_ROOT, + function(appConfig) + { + arOrder.push("2"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_CREATE_ROOT, + function(appConfig) + { + arOrder.push("3"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + var $root = $(appConfig.root); + bRootIsApp = $root.is("app"); + bRootOnPage = ($root.parents("body:first").length > 0); + bAppHtmlInRoot = ($root.find("div.test-app").length > 0); + bDone = true; + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect(arOrder.join(",")).toBe("1,2,3"); + expect(bRootIsApp).toBe(true); + expect(bRootOnPage).toBe(true); + expect(bAppHtmlInRoot).toBe(true); + }); + } + ); + + it( + 'allows manipulation of appConfig.root through out appCreateRoot methods.', + function() { + var bDone = false; + var arOrder = []; + var bRootIsApp = false; + var bRootOnPage = false; + var bAppHtmlInRoot = false; + var bHasBlueClass = false; + var bHasRedClass = false; + var bHasTestAttr = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_CREATE_ROOT, + function(appConfig) + { + appConfig.root = $("").get(0); + arOrder.push("1"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_CREATE_ROOT, + function(appConfig) + { + $(appConfig.root).addClass("blue"); + arOrder.push("2"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_CREATE_ROOT, + function(appConfig) + { + $(appConfig.root).addClass("red").attr("data-test", "test"); + arOrder.push("3"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + var $root = $(appConfig.root); + bRootIsApp = $root.is("app"); + bRootOnPage = ($root.parents("body:first").length > 0); + bAppHtmlInRoot = ($root.find("div.test-app").length > 0); + bHasBlueClass = $root.hasClass("blue"); + bHasRedClass = $root.hasClass("red"); + bHasTestAttr = !!$root.attr("data-test"); + bDone = true; + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect(bHasBlueClass).toBe(true); + expect(bHasRedClass).toBe(true); + expect(bHasTestAttr).toBe(true); + expect(arOrder.join(",")).toBe("1,2,3"); + expect(bRootIsApp).toBe(true); + expect(bRootOnPage).toBe(true); + expect(bAppHtmlInRoot).toBe(true); + }); + } + ); + + it( + 'allows resetting of appConfig.root.', + function() { + var bDone = false; + var arOrder = []; + var bRootIsApp = false; + var bRootOnPage = false; + var bAppHtmlInRoot = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_CREATE_ROOT, + function(appConfig) + { + appConfig.root = $("").get(0); + arOrder.push("1"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_CREATE_ROOT, + function(appConfig) + { + appConfig.root = $("").get(0); + arOrder.push("2"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + var $root = $(appConfig.root); + bRootIsApp = $root.is("specialapp"); + bRootOnPage = ($root.parents("body:first").length > 0); + bAppHtmlInRoot = ($root.find("div.test-app").length > 0); + bDone = true; + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect(arOrder.join(",")).toBe("1,2"); + expect(bRootIsApp).toBe(true); + expect(bRootOnPage).toBe(true); + expect(bAppHtmlInRoot).toBe(true); + }); + } + ); + +}); + +describe('F2.AppHandlers - rendering - appRenderBefore', function() { + + var containerAppHandlerToken = null; + + var async = new AsyncSpec(this); + async.beforeEachReloadF2(function() { if(F2.AppHandlers.getToken) { containerAppHandlerToken = F2.AppHandlers.getToken(); } }); + + var appConfig = function() + { + return { + appId: TEST_APP_ID, + manifestUrl: TEST_MANIFEST_URL + }; + }; + + var appManifest = function() + { + return { + scripts:[], + styles:[], + inlineScripts:[], + apps:[ + { + html: '
    Testing
    ' + } + ] + }; + }; + + it( + 'should pass appConfig as only argument to appRenderBefore.', + function() { + var bDone = false; + var bHasAppConfig = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_BEFORE, + function(appConfig) + { + bHasAppConfig = (arguments.length == 1 && appConfig && appConfig.appId && appConfig.manifestUrl) ? true : false; + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + bDone = true; + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect(bHasAppConfig).toBe(true); + }); + } + ); + + it( + 'should create appRoot using the apps html if appCreateRoot event is not bound and render appRoot to the page automatically.', + function() { + var bDone = false; + var bRootOnPage = false; + var bAppHtmlInRoot = false; + var bHasTestAppClass = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_BEFORE, + function(appConfig) + { + var $root = $(appConfig.root); + bRootOnPage = ($root.parents("body:first").length > 0); + bHasTestAppClass = $root.hasClass("test-app"); + bAppHtmlInRoot = ($root.text() == "Testing"); + bDone = true; + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + var $root = $(appConfig.root); + bRootOnPage = ($root.parents("body:first").length > 0); + bHasTestAppClass = $root.hasClass("test-app"); + bAppHtmlInRoot = ($root.text() == "Testing"); + bDone = true; + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect(bHasTestAppClass).toBe(true); + expect(bRootOnPage).toBe(true); + expect(bAppHtmlInRoot).toBe(true); + }); + } + ); + + + + it( + 'respects appRenderBefore setting appConfig.root and appends app html by default.', + function() { + var bDone = false; + var bRootIsH1 = false; + var bRootOnPage = false; + var bAppHtmlInRoot = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_BEFORE, + function(appConfig) + { + appConfig.root = $("

    ").get(0); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + var $root = $(appConfig.root); + bRootIsH1 = $root.is("h1"); + bRootOnPage = ($root.parents("body:first").length > 0); + bAppHtmlInRoot = ($root.find("div.test-app").length > 0); + bDone = true; + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect(bRootIsH1).toBe(true); + expect(bRootOnPage).toBe(true); + expect(bAppHtmlInRoot).toBe(true); + }); + } + ); + + it( + 'fires appRenderBefore functions sequentially.', + function() { + var bDone = false; + var arOrder = []; + var bRootIsApp = false; + var bRootOnPage = false; + var bAppHtmlInRoot = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_BEFORE, + function(appConfig) + { + appConfig.root = $("").get(0); + arOrder.push("1"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_BEFORE, + function(appConfig) + { + arOrder.push("2"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_BEFORE, + function(appConfig) + { + arOrder.push("3"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + var $root = $(appConfig.root); + bRootIsApp = $root.is("app"); + bRootOnPage = ($root.parents("body:first").length > 0); + bAppHtmlInRoot = ($root.find("div.test-app").length > 0); + bDone = true; + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect(arOrder.join(",")).toBe("1,2,3"); + expect(bRootIsApp).toBe(true); + expect(bRootOnPage).toBe(true); + expect(bAppHtmlInRoot).toBe(true); + }); + } + ); + + it( + 'allows manipulation of appConfig.root through out appRenderBefore methods.', + function() { + var bDone = false; + var arOrder = []; + var bRootIsApp = false; + var bRootOnPage = false; + var bAppHtmlInRoot = false; + var bHasBlueClass = false; + var bHasRedClass = false; + var bHasTestAttr = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_BEFORE, + function(appConfig) + { + appConfig.root = $("").get(0); + arOrder.push("1"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_BEFORE, + function(appConfig) + { + $(appConfig.root).addClass("blue"); + arOrder.push("2"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_BEFORE, + function(appConfig) + { + $(appConfig.root).addClass("red").attr("data-test", "test"); + arOrder.push("3"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + var $root = $(appConfig.root); + bRootIsApp = $root.is("app"); + bRootOnPage = ($root.parents("body:first").length > 0); + bAppHtmlInRoot = ($root.find("div.test-app").length > 0); + bHasBlueClass = $root.hasClass("blue"); + bHasRedClass = $root.hasClass("red"); + bHasTestAttr = !!$root.attr("data-test"); + bDone = true; + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect(bHasBlueClass).toBe(true); + expect(bHasRedClass).toBe(true); + expect(bHasTestAttr).toBe(true); + expect(arOrder.join(",")).toBe("1,2,3"); + expect(bRootIsApp).toBe(true); + expect(bRootOnPage).toBe(true); + expect(bAppHtmlInRoot).toBe(true); + }); + } + ); + + it( + 'allows resetting of appConfig.root.', + function() { + var bDone = false; + var arOrder = []; + var bRootIsApp = false; + var bRootOnPage = false; + var bAppHtmlInRoot = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_BEFORE, + function(appConfig) + { + appConfig.root = $("").get(0); + arOrder.push("1"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_BEFORE, + function(appConfig) + { + appConfig.root = $("").get(0); + arOrder.push("2"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + var $root = $(appConfig.root); + bRootIsApp = $root.is("specialapp"); + bRootOnPage = ($root.parents("body:first").length > 0); + bAppHtmlInRoot = ($root.find("div.test-app").length > 0); + bDone = true; + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect(arOrder.join(",")).toBe("1,2"); + expect(bRootIsApp).toBe(true); + expect(bRootOnPage).toBe(true); + expect(bAppHtmlInRoot).toBe(true); + }); + } + ); + +}); + +describe('F2.AppHandlers - rendering - appRender', function() { + + var containerAppHandlerToken = null; + + var async = new AsyncSpec(this); + async.beforeEachReloadF2(function() { if(F2.AppHandlers.getToken) { containerAppHandlerToken = F2.AppHandlers.getToken(); } }); + + var appConfig = function() + { + return { + appId: TEST_APP_ID, + manifestUrl: TEST_MANIFEST_URL + }; + }; + + var appManifest = function() + { + return { + scripts:[], + styles:[], + inlineScripts:[], + apps:[ + { + html: '
    Testing
    ' + } + ] + }; + }; + + it( + 'should pass appConfig and html as only arguments to appRender.', + function() { + var bDone = false; + var bHasAppConfig = false; + var bHasHtml = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_CREATE_ROOT, + function(appConfig) + { + appConfig.root = $("
    ").get(0); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER, + function(appConfig, html) + { + bHasAppConfig = (arguments.length == 2 && appConfig && appConfig.appId && appConfig.manifestUrl) ? true : false; + bHasHtml = (arguments.length == 2 && html && typeof(html) === "string") ? true : false; + var $root = $(appConfig.root); + $root.append(html); + $("body").append($root); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + bDone = true; + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect(bHasAppConfig).toBe(true); + expect(bHasHtml).toBe(true); + }); + } + ); + + it( + 'should automatically create appRoot from app html and add app to the page if no appRender method is bound.', + function() { + var bDone = false; + var bRootOnPage = false; + var bAppIsRoot = false; + var bAppHtmlInRoot = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + var $root = $(appConfig.root); + bRootOnPage = ($root.parents("body:first").length > 0); + bAppIsRoot = $root.hasClass("test-app"); + bAppHtmlInRoot = ($root.text() == "Testing"); + bDone = true; + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect(bAppIsRoot).toBe(true); + expect(bRootOnPage).toBe(true); + expect(bAppHtmlInRoot).toBe(true); + }); + } + ); + + it( + 'respects appRender appending html and putting it on the page manually.', + function() { + var bDone = false; + var bRootIsApp = false; + var bRootOnPage = false; + var bRootInParent = false; + var bAppHtmlInRoot = false; + + $("div.app-area").remove(); + $("
    ").appendTo("body"); + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_CREATE_ROOT, + function(appConfig) + { + appConfig.root = $("").get(0); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER, + function(appConfig, html) + { + var $root = $(appConfig.root); + $root.append(html); + + $("body div.app-area:first").append($root); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + var $root = $(appConfig.root); + bRootIsApp = $root.is("app"); + bRootOnPage = ($root.parents("body:first").length > 0); + bRootInParent = $root.parent().is("div.app-area"); + bAppHtmlInRoot = ($root.find("div.test-app").length > 0); + bDone = true; + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect(bRootIsApp).toBe(true); + expect(bRootOnPage).toBe(true); + expect(bRootInParent).toBe(true); + expect(bAppHtmlInRoot).toBe(true); + }); + } + ); + + it( + 'allows dom node to be only argument to appRender. Which renders the app to the dom node.', + function() { + var bDone = false; + var bRootIsApp = false; + var bRootOnPage = false; + var bRootInParent = false; + var bAppHtmlInRoot = false; + + // append a placeholder for the app + $("
    ").appendTo("body"); + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_CREATE_ROOT, + function(appConfig) + { + appConfig.root = $("").get(0); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER, + $("div.app-area:last").get(0) + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + var $root = $(appConfig.root); + bRootIsApp = $root.is("app"); + bRootOnPage = ($root.parents("body:first").length > 0); + bRootInParent = $root.parent().is("div.app-area"); + bAppHtmlInRoot = ($root.find("div.test-app").length > 0); + bDone = true; + $("div.app-area").remove(); + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect(bRootIsApp).toBe(true); + expect(bRootOnPage).toBe(true); + expect(bRootInParent).toBe(true); + expect(bAppHtmlInRoot).toBe(true); + }); + } + ); + + it( + 'should allow dom node to be only argument to appRender. Which renders the app to the dom node without needing to specifiy appCreateRoot handler.', + function() { + var bDone = false; + var bRootOnPage = false; + var bRootInParent = false; + var bRootIsAppHtml = false; + + // append a placeholder for the app + $("
    ").appendTo("body"); + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER, + $("div.app-area:last").get(0) + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + var $root = $(appConfig.root); + bRootOnPage = ($root.parents("body:first").length > 0); + bRootInParent = $root.parent().is("div.app-area"); + bRootIsAppHtml = $root.hasClass("test-app"); + bDone = true; + $("div.app-area").remove(); + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect(bRootOnPage).toBe(true); + expect(bRootInParent).toBe(true); + expect(bRootIsAppHtml).toBe(true); + }); + } + ); + + it( + 'fires appRender functions sequentially.', + function() { + var bDone = false; + var arOrder = []; + var bRootIsApp = false; + var bRootOnPage = false; + var bRootInParent = false; + var bAppHtmlInRoot = false; + + // append a placeholder for the app + $("
    ").appendTo("body"); + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_CREATE_ROOT, + function(appConfig) + { + appConfig.root = $("").get(0); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER, + function(appConfig) + { + arOrder.push("1"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER, + function(appConfig) + { + arOrder.push("2"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER, + $("div.app-area").get(0) + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER, + function(appConfig) + { + arOrder.push("3"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + var $root = $(appConfig.root); + bRootIsApp = $root.is("app"); + bRootOnPage = ($root.parents("body:first").length > 0); + bRootInParent = $root.parent().is("div.app-area"); + bAppHtmlInRoot = ($root.find("div.test-app").length > 0); + bDone = true; + $("div.app-area").remove(); + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect(arOrder.join(",")).toBe("1,2,3"); + expect(bRootIsApp).toBe(true); + expect(bRootOnPage).toBe(true); + expect(bRootInParent).toBe(true); + expect(bAppHtmlInRoot).toBe(true); + }); + } + ); + + it( + 'allows manipulation of appConfig.root through out appRender methods.', + function() { + var bDone = false; + var arOrder = []; + var bRootIsApp = false; + var bRootOnPage = false; + var bAppHtmlInRoot = false; + var bHasBlueClass = false; + var bHasRedClass = false; + var bHasTestAttr = false; + var bRootInParent = false; + + $("
    ").appendTo("body"); + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_CREATE_ROOT, + function(appConfig) + { + appConfig.root = $("").get(0); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER, + function(appConfig) + { + arOrder.push("1"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER, + function(appConfig) + { + $(appConfig.root).addClass("blue"); + arOrder.push("2"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER, + $("div.app-area").get(0) + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER, + function(appConfig) + { + $(appConfig.root).addClass("red").attr("data-test", "test"); + arOrder.push("3"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + var $root = $(appConfig.root); + bRootIsApp = $root.is("app"); + bRootOnPage = ($root.parents("body:first").length > 0); + bAppHtmlInRoot = ($root.find("div.test-app").length > 0); + bHasBlueClass = $root.hasClass("blue"); + bRootInParent = $root.parent().is("div.app-area"); + bHasRedClass = $root.hasClass("red"); + bHasTestAttr = !!$root.attr("data-test"); + bDone = true; + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect(bHasBlueClass).toBe(true); + expect(bHasRedClass).toBe(true); + expect(bHasTestAttr).toBe(true); + expect(arOrder.join(",")).toBe("1,2,3"); + expect(bRootIsApp).toBe(true); + expect(bRootOnPage).toBe(true); + expect(bAppHtmlInRoot).toBe(true); + expect(bRootInParent).toBe(true); + }); + } + ); + +}); + +describe('F2.AppHandlers - rendering - appRenderAfter', function() { + + var containerAppHandlerToken = null; + + var async = new AsyncSpec(this); + async.beforeEachReloadF2(function() { if(F2.AppHandlers.getToken) { containerAppHandlerToken = F2.AppHandlers.getToken(); } }); + + var appConfig = function() + { + return { + appId: TEST_APP_ID, + manifestUrl: TEST_MANIFEST_URL + }; + }; + + var appManifest = function() + { + return { + scripts:[], + styles:[], + inlineScripts:[], + apps:[ + { + html: '
    Testing
    ' + } + ] + }; + }; + + it( + 'should pass appConfig as only argument to appRenderAfter.', + function() { + var bDone = false; + var bHasAppConfig = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + bHasAppConfig = (arguments.length == 1 && appConfig && appConfig.appId && appConfig.manifestUrl) ? true : false; + bDone = true; + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect(bHasAppConfig).toBe(true); + }); + } + ); + + it( + 'should fire appRenderAfter only after app is in dom.', + function() { + var bDone = false; + var bRootOnPage = false; + var bAppIsRoot = false; + var bAppHtmlInRoot = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + var $root = $(appConfig.root); + bRootOnPage = ($root.parents("body:first").length > 0); + bAppIsRoot = $root.hasClass("test-app"); + bAppHtmlInRoot = ($root.text() == "Testing"); + bDone = true; + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect(bAppIsRoot).toBe(true); + expect(bRootOnPage).toBe(true); + expect(bAppHtmlInRoot).toBe(true); + }); + } + ); + + it( + 'fires appRenderAfter functions sequentially.', + function() { + var bDone = false; + var arOrder = []; + var bRootOnPage = false; + var bAppHtmlInRoot = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + arOrder.push("1"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + arOrder.push("2"); + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + arOrder.push("3"); + var $root = $(appConfig.root); + bRootOnPage = ($root.parents("body:first").length > 0); + bAppHtmlInRoot = ($root.text() == "Testing"); + bDone = true; + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bDone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect(arOrder.join(",")).toBe("1,2,3"); + expect(bRootOnPage).toBe(true); + expect(bAppHtmlInRoot).toBe(true); + }); + } + ); + +}); + +describe('F2.AppHandlers - rendering - appDestroyBefore', function() { + var containerAppHandlerToken = null; + + var async = new AsyncSpec(this); + async.beforeEachReloadF2(function() { if(F2.AppHandlers.getToken) { containerAppHandlerToken = F2.AppHandlers.getToken(); } }); + + var appConfig = function() + { + return { + appId: TEST_APP_ID, + manifestUrl: TEST_MANIFEST_URL + }; + }; + + var appManifest = function() + { + return { + scripts:[], + styles:[], + inlineScripts:[], + apps:[ + { + html: '
    Testing
    ' + } + ] + }; + }; + + it( + 'should remove on() appDestroyBefore handlers regardless of namespace if no namespace passed to off() event.', + function() { + var bAppStillAround = false; + var bAppGone = false; + var $root = null; + var bAppDestroyOnMethodCalled = false; + var bAppDestroyWithNamespaceOnMethodCalled = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_DESTROY_BEFORE, + function() + { + bAppDestroyOnMethodCalled = true; + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_DESTROY_BEFORE + ".specialNamespace", + function() + { + bAppDestroyWithNamespaceOnMethodCalled = true; + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + $root = $(appConfig.root); + setTimeout(function() { bAppGone = true; }, 700); + F2.AppHandlers.off(containerAppHandlerToken, F2.Constants.AppHandlers.APP_DESTROY_BEFORE); + F2.removeApp(appConfig.instanceId); + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bAppGone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect($root.parent().length == 0).toBe(true); + expect(bAppDestroyOnMethodCalled).toBe(false); + expect(bAppDestroyWithNamespaceOnMethodCalled).toBe(false); + }); + } + ); + + it( + 'should only remove on() from appDestroyBefore handlers if namespace matches what was passed to off() event.', + function() { + var bAppStillAround = false; + var bAppGone = false; + var $root = null; + var bAppDestroyOnMethodCalled = false; + var bAppDestroyWithNamespaceOnMethodCalled = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_DESTROY_BEFORE, + function() + { + bAppDestroyOnMethodCalled = true; + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_DESTROY_BEFORE + ".specialNamespace", + function() + { + bAppDestroyWithNamespaceOnMethodCalled = true; + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + $root = $(appConfig.root); + setTimeout(function() { bAppGone = true; }, 700); + F2.AppHandlers.off(containerAppHandlerToken, F2.Constants.AppHandlers.APP_DESTROY_BEFORE + ".specialNamespace"); + F2.removeApp(appConfig.instanceId); + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bAppGone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect($root.parent().length == 0).toBe(true); + expect(bAppDestroyOnMethodCalled).toBe(true); + expect(bAppDestroyWithNamespaceOnMethodCalled).toBe(false); + }); + } + ); +}); + +describe('F2.AppHandlers - rendering - appDestroy', function() { + + var containerAppHandlerToken = null; + + var async = new AsyncSpec(this); + async.beforeEachReloadF2(function() { if(F2.AppHandlers.getToken) { containerAppHandlerToken = F2.AppHandlers.getToken(); } }); + + var appConfig = function() + { + return { + appId: TEST_APP_ID, + manifestUrl: TEST_MANIFEST_URL + }; + }; + + var appManifest = function() + { + return { + scripts:[], + styles:[], + inlineScripts:[], + apps:[ + { + html: '
    Testing
    ' + } + ] + }; + }; + + it( + 'should remove app from page if no appHandlers are declared.', + function() { + var bAppStillAround = false; + var bAppGone = false; + var $root = null; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + $root = $(appConfig.root); + setTimeout(function() { bAppStillAround = true; }, 100); + setTimeout(function() { bAppGone = true; }, 600); + F2.removeApp(appConfig.instanceId); + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bAppStillAround; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect($root.parent().length == 1).toBe(true); + }); + + waitsFor( + function() + { + return bAppGone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect($root.parent().length == 0).toBe(true); + }); + } + ); + + it('should call app instance .destroy() method if destory method exists.', function(){ + F2.inlineScriptsEvaluated = false; + F2.init(); + F2.registerApps([{appId:'com_openf2_tests_helloworld', manifestUrl:'/'}], [{"inlineScripts": [], "scripts":["js/test.js"],"apps":[{ html: '
    Testing
    ' }]}]); + + waitsFor( + function() + { + return F2.testAppInitialized; + }, + 'Inline scripts were never evaluated', + 3000 + ); + + runs(function() { + F2.removeApp(F2.testAppInstanceID); + + waitsFor( + function() + { + return F2.destroyAppMethodCalled; + }, + 'destroy() method was never evaluated', + 3000 + ); + + runs(function() { + expect(F2.destroyAppMethodCalled).toBe(true); + }); + }); + }); + + it( + 'should remove on() appDestroy handlers regardless of namespace if no namespace passed to off() event.', + function() { + var bAppStillAround = false; + var bAppGone = false; + var $root = null; + var bAppDestroyOnMethodCalled = false; + var bAppDestroyWithNamespaceOnMethodCalled = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_DESTROY, + function(appConfig) + { + bAppDestroyOnMethodCalled = true; + } + ) + .on( + containerAppHandlerToken, + "appDestroy.specialNamespace", + function(appConfig) + { + bAppDestroyWithNamespaceOnMethodCalled = true; + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + $root = $(appConfig.root); + setTimeout(function() { bAppGone = true; }, 600); + F2.AppHandlers.off(containerAppHandlerToken, F2.Constants.AppHandlers.APP_DESTROY); + F2.removeApp(appConfig.instanceId); + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bAppGone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect($root.parent().length == 0).toBe(true); + expect(bAppDestroyOnMethodCalled).toBe(false); + expect(bAppDestroyWithNamespaceOnMethodCalled).toBe(false); + }); + } + ); + + it( + 'should only remove on() from appDestroy handlers if namespace matches what was passed to off() event.', + function() { + var bAppStillAround = false; + var bAppGone = false; + var $root = null; + var bAppDestroyOnMethodCalled = false; + var bAppDestroyWithNamespaceOnMethodCalled = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_DESTROY, + function(appInstance) + { + // call the apps destroy method, if it has one + if(appInstance && appInstance.app && appInstance.app.destroy && typeof(appInstance.app.destroy) == "function") + { + appInstance.app.destroy(); + } + // warn the container developer/app developer that even though they have a destroy method it hasn't been + else if(appInstance && appInstance.app && appInstance.app.destroy) + { + F2.log(app.config.appId + " has a destroy property, but destroy is not of type function and as such will not be executed."); + } + + // fade out and remove the root + jQuery(appInstance.config.root).fadeOut(250, function() { + jQuery(this).remove(); + }); + + bAppDestroyOnMethodCalled = true; + } + ) + .on( + containerAppHandlerToken, + "appDestroy.specialNamespace", + function(appConfig) + { + bAppDestroyWithNamespaceOnMethodCalled = true; + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + $root = $(appConfig.root); + setTimeout(function() { bAppGone = true; }, 400); + F2.AppHandlers.off(containerAppHandlerToken, "appDestroy.specialNamespace"); + F2.removeApp(appConfig.instanceId); + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bAppGone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect($root.parent().length == 0).toBe(true); + expect(bAppDestroyOnMethodCalled).toBe(true); + expect(bAppDestroyWithNamespaceOnMethodCalled).toBe(false); + }); + } + ); +}); + +describe('F2.AppHandlers - rendering - appDestroyBefore', function() { + var containerAppHandlerToken = null; + + var async = new AsyncSpec(this); + async.beforeEachReloadF2(function() { if(F2.AppHandlers.getToken) { containerAppHandlerToken = F2.AppHandlers.getToken(); } }); + + var appConfig = function() + { + return { + appId: TEST_APP_ID, + manifestUrl: TEST_MANIFEST_URL + }; + }; + + var appManifest = function() + { + return { + scripts:[], + styles:[], + inlineScripts:[], + apps:[ + { + html: '
    Testing
    ' + } + ] + }; + }; + + it( + 'should remove on() appDestroyAfter handlers regardless of namespace if no namespace passed to off() event.', + function() { + var bAppStillAround = false; + var bAppGone = false; + var $root = null; + var bAppDestroyOnMethodCalled = false; + var bAppDestroyWithNamespaceOnMethodCalled = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_DESTROY_AFTER, + function() + { + bAppDestroyOnMethodCalled = true; + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_DESTROY_AFTER + ".specialNamespace", + function() + { + bAppDestroyWithNamespaceOnMethodCalled = true; + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + $root = $(appConfig.root); + setTimeout(function() { bAppGone = true; }, 700); + F2.AppHandlers.off(containerAppHandlerToken, F2.Constants.AppHandlers.APP_DESTROY_AFTER); + F2.removeApp(appConfig.instanceId); + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bAppGone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect($root.parent().length == 0).toBe(true); + expect(bAppDestroyOnMethodCalled).toBe(false); + expect(bAppDestroyWithNamespaceOnMethodCalled).toBe(false); + }); + } + ); + + it( + 'should only remove on() from appDestroyAfter handlers if namespace matches what was passed to off() event.', + function() { + var bAppStillAround = false; + var bAppGone = false; + var $root = null; + var bAppDestroyOnMethodCalled = false; + var bAppDestroyWithNamespaceOnMethodCalled = false; + + F2.init(); + + F2.AppHandlers + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_DESTROY_AFTER, + function() + { + bAppDestroyOnMethodCalled = true; + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_DESTROY_AFTER + ".specialNamespace", + function() + { + bAppDestroyWithNamespaceOnMethodCalled = true; + } + ) + .on( + containerAppHandlerToken, + F2.Constants.AppHandlers.APP_RENDER_AFTER, + function(appConfig) + { + $root = $(appConfig.root); + setTimeout(function() { bAppGone = true; }, 700); + F2.AppHandlers.off(containerAppHandlerToken, F2.Constants.AppHandlers.APP_DESTROY_AFTER + ".specialNamespace"); + F2.removeApp(appConfig.instanceId); + } + ); + + F2.registerApps(appConfig(), appManifest()); + + waitsFor( + function() + { + return bAppGone; + }, + 'AppHandlers.On( appRenderAfter ) was never fired', + 3000 + ); + + runs(function() { + expect($root.parent().length == 0).toBe(true); + expect(bAppDestroyOnMethodCalled).toBe(true); + expect(bAppDestroyWithNamespaceOnMethodCalled).toBe(false); + }); + } + ); +}); \ No newline at end of file diff --git a/tests/spec/console-test.js b/tests/spec/console-test.js new file mode 100644 index 00000000..10fa3699 --- /dev/null +++ b/tests/spec/console-test.js @@ -0,0 +1,14 @@ +describe("Console", function() { + it ("should fail", function() { + expect(false).toBeFalsy(); + }); + + it ("should succeed", function() { + expect(true).toBeTruthy(); + }); + + it ("jQuery exists", function() { + expect(!!jQuery).toBeTruthy(); + }); + +}); \ No newline at end of file diff --git a/tests/spec/container-spec.js b/tests/spec/container-spec.js index 8db5a19f..949db8b0 100644 --- a/tests/spec/container-spec.js +++ b/tests/spec/container-spec.js @@ -1,3 +1,173 @@ +describe('F2.registerApps - pre-load', function() { + + it('should throw exception if F2.init() is not called prior.', function() { + expect(function(){ + + var appConfig = { + appId: TEST_APP_ID, + manifestUrl: TEST_MANIFEST_URL, + root: $("body").find("div."+TEST_APP_ID+":first").get(0) + }; + + F2.registerApps([appConfig]); + }).toLog('F2.init() must be called before F2.registerApps()'); + }); + + it('should throw exception if no appConfigs are passed.', function() { + expect(function(){ + F2.init(); + F2.registerApps(); + }).toLog('At least one AppConfig must be passed when calling F2.registerApps()'); + }); + + it('should allow you to pass single appConfig as object to F2.registerApps.', function() { + expect(function(){ + F2.init(); + var appConfig = { + appId:TEST_APP_ID, + root: $("body").find("div."+TEST_APP_ID+":first").get(0) + }; + F2.registerApps(appConfig); + }).not.toThrow(); + }); + + it('should not require appConfig.manifestUrl when passing pre-load appConfig to F2.registerApps.', function() { + expect(function(){ + F2.init(); + var appConfig = { + appId: TEST_APP_ID, + root: $("body").find("div."+TEST_APP_ID+":first").get(0) + }; + F2.registerApps(appConfig); + }).not.toLog('"manifestUrl" missing from app object'); + }); + + it('should throw exception if you pass an invalid appConfig to F2.registerApps.', function() { + expect(function(){ + F2.init(); + F2.registerApps({}); + }).toLog('"appId" missing from app object'); + }); + + it('should request apps without valid root property and auto init pre-load apps with root when passing mix to F2.registerApps.', function() { + var bAfterFired = false + F2.PreloadTestComplete = false; + F2.PreloadAppInitialized = false; + F2.PreloadRetrievedEmit = false; + + var appConfigs = [ + { + appId: TEST_APP_ID2, + manifestUrl: TEST_MANIFEST_URL2 + }, + { + appId: TEST_APP_ID, + root: $("body").find('div.'+TEST_APP_ID+':first').get(0) + } + ]; + + F2.init(); + + F2.AppHandlers.on(F2.AppHandlers.getToken(), F2.Constants.AppHandlers.APP_RENDER_AFTER, function(){ bAfterFired = true; }); + + F2.registerApps(appConfigs); + + waitsFor( + function() + { + return bAfterFired; + }, + 'appRenderAfter was never fired', + 10000 + ); + + runs(function() { + F2.Events.emit("PreloadAppCommuncation", [true]); + expect(bAfterFired).toBeTruthy(); + expect(F2.PreloadTestComplete).toBe(true); + //expect(F2.PreloadRetrievedEmit).toBe(true); + }); + }); + + it('should allow you to init/register apps that are already on the page.', function() { + + F2.PreloadTestComplete = false; + F2.PreloadAppInitialized = false; + F2.PreloadRetrievedEmit = false; + + var appConfig = { + appId: TEST_APP_ID, + manifestUrl: TEST_MANIFEST_URL, + root: $("body").find("div."+TEST_APP_ID+":first").get(0) + }; + + F2.init(); + + // init is called above + F2.registerApps([appConfig]); + + waitsFor( + function() + { + return F2.PreloadAppInitialized; + }, + 'Emit retrieve was never fired', + 10000 + ); + + runs(function() { + // fires the emit to make sure the app is actually listening + F2.Events.emit("PreloadAppCommuncation", [true]); + expect(F2.PreloadTestComplete).toBe(true); + expect(F2.PreloadRetrievedEmit).toBe(true); + F2.removeApp(appConfig.removeApp); + }); + }); + + it('should allow you to init/register multiple of the same app that are already on the page.', function() { + + F2.PreloadTestComplete = false; + F2.PreloadAppInitialized = false; + F2.PreloadRetrievedEmit = false; + F2.PreloadTestCompleteCounter = 0; + F2.PreloadAppInitializedCounter = 0; + F2.PreloadRetrievedEmitCounter = 0; + + var $appsOnPage = $("body").find("div."+TEST_APP_ID); + var appConfigs = [ + { + appId:TEST_APP_ID, + manifestUrl:TEST_MANIFEST_URL, + root: $appsOnPage.get(0) + }, + { + appId:TEST_APP_ID, + manifestUrl:TEST_MANIFEST_URL, + root: $appsOnPage.get(1) + } + ]; + + // init is called above + F2.registerApps(appConfigs); + + waitsFor( + function() + { + return (F2.PreloadAppInitializedCounter == 2); + }, + 'Emit retrieve was never fired', + 10000 + ); + + runs(function() { + // fires the emit to make sure the app is actually listening + F2.Events.emit("PreloadAppCommuncation", [true]); + expect(F2.PreloadTestCompleteCounter).toBe(2); + expect(F2.PreloadRetrievedEmitCounter).toBe(2); + }); + }); +}); + describe('F2.init', function() { var async = new AsyncSpec(this); @@ -13,6 +183,53 @@ describe('F2.init', function() { }); +describe('F2.init - xhr overrides', function() { + var async = new AsyncSpec(this); + async.beforeEachReloadF2(function() { + // nothing to do after reload + }); + + it('should throw an exception when ContainerConfig.xhr is not an object or function', function() { + expect(function() { + F2.init({ + xhr: true + }); + }).toThrow('ContainerConfig.xhr should be a function or an object'); + }); + + it('should throw an exception when xhr.dataType is not a function', function() { + expect(function() { + F2.init({ + xhr: { + dataType: true + } + }); + }).toThrow(new Error('ContainerConfig.xhr.dataType should be a function')); + }); + + it('should throw an exception when xhr.type is not a function', function() { + expect(function() { + F2.init({ + xhr: { + type: true + } + }); + F2.registerApps(appConfig); + }).toThrow(new Error('ContainerConfig.xhr.type should be a function')); + }); + + it('should throw an exception when xhr.url is not a function', function() { + expect(function() { + F2.init({ + xhr: { + url: true + } + }); + F2.registerApps(appConfig); + }).toThrow(new Error('ContainerConfig.xhr.url should be a function')); + }); +}); + describe('F2.isInit', function() { var async = new AsyncSpec(this); @@ -38,7 +255,7 @@ describe('F2.registerApps - basic', function() { it('should fail on empty parameters', function() { expect(function() { F2.registerApps(); - }).toLog('At least one AppConfig must be passed when calling F2.registerApps()'); + }).toLog('At least one AppConfig must be passed when calling F2.registerApps()'); }); it('should fail when passed an empty array', function() { @@ -59,13 +276,13 @@ describe('F2.registerApps - basic', function() { }).toLog('"appId" missing from app object'); expect(function() { - F2.registerApps({appId:'com_openf2_tests_helloworld'}); - }).toLog('manifestUrl" missing from app object'); + F2.registerApps({appId:TEST_APP_ID}); + }).toLog('"manifestUrl" missing from app object'); }); it('should fail when the parameter lengths do not match', function() { expect(function() { - F2.registerApps({appId:'com_openf2_tests_helloworld', manifestUrl:'http://www.openf2.org'}, [{}, {}]); + F2.registerApps({appId:TEST_APP_ID, manifestUrl:TEST_MANIFEST_URL}, [{}, {}]); }).toLog('The length of "apps" does not equal the length of "appManifests"'); }); @@ -77,8 +294,8 @@ describe('F2.registerApps - basic', function() { }; runs(function() { - F2.registerApps({appId:'com_openf2_tests_helloworld', manifestUrl:'http://www.openf2.org'}, {apps:[{html:'
    '}]}); - }) + F2.registerApps({appId:TEST_APP_ID, manifestUrl:TEST_MANIFEST_URL}, {apps:[{html:'
    '}]}); + }); // wait long enough for registerApps to have failed waits(1000); @@ -86,8 +303,232 @@ describe('F2.registerApps - basic', function() { // F2.log should not have run runs(function() { expect(passedMessage).toBeFalsy(); + }); + }); +}); + +describe('F2.registerApps - xhr overrides', function() { + + var async = new AsyncSpec(this); + async.beforeEachReloadF2(function() { + // nothing to do after reload + }); + + var appConfig = { + appId: TEST_APP_ID, + manifestUrl: TEST_MANIFEST_URL + }; + + it('should call xhr if it is defined', function() { + var isFired = false; + runs(function() { + F2.init({ + xhr: function(url, apps, success, error, complete) { + isFired = true; + } + }) + F2.registerApps(appConfig); + }); + waitsFor(function() { + return isFired; + }); + runs(function() { + expect(isFired).toBeTruthy(); }) }); + + it('should pass 5 parameters to xhr', function() { + var isFired = false, + numArgs = 0, + urlParam, appConfigsParam, successParam, errorParam, completeParam; + + runs(function() { + F2.init({ + xhr: function(url, appConfigs, success, error, complete) { + numArgs = arguments.length; + urlParam = url; + appConfigsParam = appConfigs; + successParam = success; + errorParam = error; + completeParam = complete; + + isFired = true; + } + }); + F2.registerApps(appConfig); + }); + + waitsFor(function() { + return isFired; + }); + + runs(function() { + expect(numArgs).toBe(5); + expect(typeof urlParam).toBe('string'); + expect(appConfigsParam instanceof Array).toBeTruthy(); + expect(typeof successParam).toBe('function'); + expect(typeof errorParam).toBe('function'); + expect(typeof completeParam).toBe('function'); + }) + }); + + it('should call xhr.dataType', function() { + var isFired = false; + runs(function() { + F2.init({ + xhr: { + dataType: function() { + isFired = true; + return ''; + } + } + }); + F2.registerApps(appConfig); + }); + waitsFor(function() { + return isFired; + }, 'xhr.dataType was not fired', 10000); + runs(function() { + expect(isFired).toBeTruthy(); + }); + }); + + it('should throw an exception when xhr.dataType does not return a string', function() { + expect(function() { + F2.init({ + xhr: { + dataType: function() {} + } + }); + F2.registerApps(appConfig); + }).toThrow(new Error('ContainerConfig.xhr.dataType should return a string')); + }); + + it('should call xhr.type', function() { + var isFired = false; + F2.init({ + xhr: { + type: function() { + isFired = true; + return ''; + } + } + }); + F2.registerApps(appConfig); + waitsFor(function() { + return isFired; + }, 'xhr.type was not fired', 10000); + runs(function() { + expect(isFired).toBeTruthy(); + }); + }); + + it('should throw an exception when xhr.type does not return a string', function() { + expect(function() { + F2.init({ + xhr: { + type: function() {} + } + }); + F2.registerApps(appConfig); + }).toThrow(new Error('ContainerConfig.xhr.type should return a string')); + }); + + it('should call xhr.url', function() { + var isFired = false; + F2.init({ + xhr: { + url: function() { + isFired = true; + return ''; + } + } + }); + F2.registerApps(appConfig); + waitsFor(function() { + return isFired; + }, 'xhr.url was not fired', 10000); + runs(function() { + expect(isFired).toBeTruthy(); + }); + }); + + it('should throw an exception when xhr.url does not return a string', function() { + expect(function() { + F2.init({ + xhr: { + url: function() {} + } + }); + F2.registerApps(appConfig); + }).toThrow(new Error('ContainerConfig.xhr.url should return a string')); + }); + + itConditionally(window.F2_NODE_TEST_SERVER, 'should use POST when the domain of the container matches that of the app (#41, #59)', function() { + + var isPost = false, + hasReturned = false; + F2.log = function(message) { + hasReturned = true; + isPost = message; + }; + + runs(function() { + F2.init({ + xhr: { + dataType: function(url) { + return F2.isLocalRequest(url) ? 'json' : 'jsonp'; + }, + type: function(url) { + return F2.isLocalRequest(url) ? 'POST' : 'GET'; + } + } + }); + F2.registerApps({appId:'com_test_app', manifestUrl:'http://localhost:8080/httpPostTest'}); + }); + + // wait for registerApps to complete and load the app + waitsFor(function() { + return hasReturned; + }, 'test app was never loaded', 10000); + + runs(function() { + expect(isPost).toBeTruthy(); + }); + }); + + itConditionally(window.F2_NODE_TEST_SERVER, 'should use GET when the domain of the container does not match that of the app (#41, #59)', function() { + + var isPost = false, + hasReturned = false; + F2.log = function(message) { + hasReturned = true; + isPost = message; + }; + + runs(function() { + F2.init({ + xhr: { + dataType: function(url) { + return F2.isLocalRequest(url) ? 'json' : 'jsonp'; + }, + type: function(url) { + return F2.isLocalRequest(url) ? 'POST' : 'GET'; + } + } + }); + F2.registerApps({appId:'com_test_app', manifestUrl:'http://127.0.0.1:8080/httpPostTest'}); + }); + + // wait for registerApps to complete and load the app + waitsFor(function() { + return hasReturned; + }, 'test app was never loaded', 10000); + + runs(function() { + expect(isPost).toBeFalsy(); + }); + }); }); describe('F2.registerApps - rendering', function() { @@ -98,8 +539,8 @@ describe('F2.registerApps - rendering', function() { }); var appConfig = { - appId: 'com_openf2_tests_helloworld', - manifestUrl: 'http://www.openf2.org' + appId: TEST_APP_ID, + manifestUrl: TEST_MANIFEST_URL }; var appManifest = { scripts:[], @@ -124,8 +565,8 @@ describe('F2.registerApps - rendering', function() { return isFired; }, 'beforeAppRender was never fired', 10000); runs(function() { - expect(isFired).toBeTruthy(); - }) + expect(isFired).toBe(true); + }); }); it('should allow beforeAppRender to return null', function() { @@ -137,16 +578,42 @@ describe('F2.registerApps - rendering', function() { F2.registerApps(appConfig, [appManifest]); }); - /* + it('should eval AppManifest.inlineScripts when AppManifest.scripts are defined', function(){ + F2.inlineScriptsEvaluated = false; F2.init(); - F2.registerApps([{appId:'com_openf2_tests_helloworld', manifestUrl:'/'}], [{"inlineScripts": ["(function(){F2.inlineScriptsEvaluated=true;})()"], "scripts":["http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/js/bootstrap.min.js"],"apps":[{}]}]); - expect(F2.inlineScriptsEvaluated).not.toBeUndefined(); + F2.registerApps([{appId:TEST_APP_ID, manifestUrl:TEST_MANIFEST_URL}], [{'inlineScripts': ['(function(){F2.inlineScriptsEvaluated=true;})()'], 'scripts':['js/test.js'],'apps':[{'html': '
    Testing
    ' }]}]); + + waitsFor( + function() + { + return F2.inlineScriptsEvaluated; + }, + 'Inline scripts were never evaluated', + 10000 + ); + + runs(function() { + expect(F2.inlineScriptsEvaluated).toBe(true); + }); + }); - */ + it('should eval AppManifest.inlineScripts when AppManifest.scripts are not defined', function(){ + F2.inlineScriptsEvaluated = false; F2.init(); - F2.registerApps([{appId:'com_openf2_tests_helloworld', manifestUrl:'/'}], [{"inlineScripts": ["(function(){F2.inlineScriptsEvaluated=true;})()"],"apps":[{}]}]); - expect(F2.inlineScriptsEvaluated).not.toBeUndefined(); + F2.registerApps([{appId:TEST_APP_ID, manifestUrl:TEST_MANIFEST_URL}], [{'inlineScripts': ['(function(){F2.inlineScriptsEvaluated=true;})()'],'apps':[{'html': '
    Testing
    ' }]}]); + waitsFor( + function() + { + return F2.inlineScriptsEvaluated; + }, + 'Inline scripts were never evaluated', + 10000 + ); + + runs(function() { + expect(F2.inlineScriptsEvaluated).toBe(true); + }); }); }); \ No newline at end of file diff --git a/tests/spec/spec-helpers.js b/tests/spec/spec-helpers.js index ac4e7855..6a49e505 100644 --- a/tests/spec/spec-helpers.js +++ b/tests/spec/spec-helpers.js @@ -1,10 +1,16 @@ +var TEST_MANIFEST_URL = 'http://docs.openf2.org/demos/apps/JavaScript/HelloWorld/manifest.js', + TEST_APP_ID = 'com_openf2_examples_javascript_helloworld', + TEST_MANIFEST_URL2 = 'http://www.openf2.org/Examples/Apps', + TEST_APP_ID2 = 'com_openf2_examples_csharp_marketnews' +; + /** * Addition to Jasmine Async that reloads F2 */ AsyncSpec.prototype.beforeEachReloadF2 = function(callback) { this.beforeEach(function(done) { $.ajax({ - url: '../sdk/f2.debug.js', + url: '../sdk/f2.min.js', dataType: 'script', complete: function() { callback && callback(); @@ -14,6 +20,22 @@ AsyncSpec.prototype.beforeEachReloadF2 = function(callback) { }); }; +/** + * + */ +itConditionally = function(condition, desc, func) { + if (condition) { + return jasmine.getEnv().it(desc, func); + } else { + var el = document.getElementById('tests-skipped'); + var count = Number(el.getAttribute('data-count')) + 1; + el.innerHTML = 'Skipping ' + count + ' spec' + ((count > 1) ? 's' : ''); + el.setAttribute('data-count', count); + el.style.display = 'block'; + return jasmine.getEnv().xit(desc, func); + } +}; + /** * Clean out the test fixture before each spec */