diff --git a/.eslintrc b/.eslintrc new file mode 100755 index 0000000..7e5afc6 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,185 @@ +{ + parserOptions: { + ecmaVersion: 6, + sourceType: 'module' + }, + env: { + node: true, + es6: true + }, + rules: { + // Possible Errors + 'comma-dangle': [2, 'never'], + 'no-cond-assign': 2, + 'no-constant-condition': 2, + 'no-control-regex': 2, + 'no-debugger': 2, + 'no-dupe-args': 2, + 'no-dupe-keys': 2, + 'no-duplicate-case': 2, + 'no-empty-character-class': 2, + + 'no-ex-assign': 2, + 'no-extra-boolean-cast': 2, + + 'no-extra-semi': 2, + 'no-func-assign': 2, + 'no-inner-declarations': 2, + 'no-invalid-regexp': 2, + 'no-irregular-whitespace': 2, + 'no-negated-in-lhs': 2, + 'no-obj-calls': 2, + 'no-regex-spaces': 2, + 'no-sparse-arrays': 2, + 'no-unreachable': 2, + 'use-isnan': 2, + 'valid-typeof': 2, + 'no-unexpected-multiline': 2, + + // Best Practices + 'accessor-pairs': 2, + 'array-callback-return': 2, + 'block-scoped-var': 2, + 'curly': 2, + 'default-case': 2, + 'dot-notation': 2, + 'dot-location': [2, 'property'], + 'eqeqeq': 2, + 'guard-for-in': 2, + 'no-alert': 2, + 'no-caller': 2, + 'no-case-declarations': 2, + 'no-div-regex': 2, + 'no-else-return': 2, + 'no-empty-pattern': 2, + 'no-eq-null': 2, + 'no-eval': 2, + 'no-extend-native': 2, + 'no-extra-bind': 2, + 'no-extra-label': 2, + 'no-fallthrough': 2, + 'no-floating-decimal': 2, + 'no-implicit-coercion': 2, + 'no-implicit-globals': 2, + 'no-implied-eval': 2, + 'no-iterator': 2, + 'no-labels': 2, + 'no-lone-blocks': 2, + 'no-loop-func': 2, + + 'no-multi-spaces': 0, + 'no-multi-str': 0, + 'no-native-reassign': 2, + 'no-new-func': 2, + 'no-new-wrappers': 2, + 'no-new': 2, + 'no-octal-escape': 2, + 'no-octal': 2, + 'no-proto': 2, + 'no-redeclare': 2, + 'no-return-assign': [2, 'always'], + 'no-script-url': 2, + 'no-self-assign': 2, + 'no-self-compare': 2, + 'no-sequences': 2, + 'no-throw-literal': 2, + 'no-unmodified-loop-condition': 2, + 'no-unused-expressions': 2, + 'no-unused-labels': 2, + 'no-useless-call': 2, + 'no-useless-concat': 2, + 'no-void': 2, + 'no-warning-comments': 1, + 'no-with': 2, + 'radix': 2, + 'require-jsdoc': 0, + 'valid-jsdoc': [2, { + requireReturn: false, + prefer: { + returns: 'return' + } + }], + 'wrap-iife': [2, 'inside'], + 'yoda': 2, + + // Variables + 'no-delete-var': 2, + 'no-label-var': 2, + 'no-shadow-restricted-names': 2, + 'no-undef-init': 2, + 'no-undef': [2, {typeof: true}], + 'no-unused-vars': 0, + 'no-use-before-define': [2, 'nofunc'], + + // Node.js + + 'handle-callback-err': 0, + 'no-mixed-requires': [2, {grouping: true, allowCall: true}], + 'no-new-require': 2, + 'no-path-concat': 2, + 'no-restricted-imports': [2, 'domain', 'freelist', 'smalloc', 'sys', 'colors'], + 'no-restricted-modules': [2, 'domain', 'freelist', 'smalloc', 'sys', 'colors'], + + // Stylistic Issues + 'array-bracket-spacing': [2, 'never'], + 'brace-style': ["error", "stroustrup", { "allowSingleLine": true }], + 'camelcase': [2, {properties: 'always'}], + 'comma-spacing': [2, {before: false, after: true}], + 'comma-style': [2, 'last'], + 'computed-property-spacing': [2, 'never'], + 'eol-last': 2, + 'indent': [2, 2, { + SwitchCase: 1 + }], + 'jsx-quotes': 2, + 'key-spacing': [2, {beforeColon: false, afterColon: true}], + 'keyword-spacing': 2, + 'linebreak-style': [2, 'unix'], + 'max-len': 0, + 'max-nested-callbacks': [1, 4], + 'new-cap': [2, {newIsCap: true, capIsNew: true}], + 'new-parens': 2, + 'no-array-constructor': 2, + 'no-lonely-if': 2, + 'no-mixed-spaces-and-tabs': 2, + 'no-multiple-empty-lines': 0, + 'no-nested-ternary': 2, + 'no-negated-condition': 2, + 'no-new-object': 2, + 'no-restricted-syntax': [2, 'WithStatement'], + 'no-whitespace-before-property': 2, + 'no-spaced-func': 2, + 'no-trailing-spaces': 2, + 'no-unneeded-ternary': 2, + 'object-curly-spacing': [2, "always"], + 'one-var': [2, 'never'], + 'one-var-declaration-per-line': 2, + 'operator-assignment': [2, 'always'], + 'operator-linebreak': [2, 'after'], + 'padded-blocks': 0, + 'quote-props': [2, 'consistent-as-needed'], + 'quotes': [2, 'single'], + 'semi-spacing': [2, {before: false, after: true}], + 'semi': [2, 'always'], + 'space-before-blocks': [2, 'always'], + 'space-before-function-paren': [2, 'never'], + 'space-in-parens': [2, 'never'], + 'space-infix-ops': 2, + 'space-unary-ops': 2, + 'spaced-comment': [2, 'always', {markers: ['!']}], + + // ES2015 + 'arrow-parens': [2, 'always'], + 'arrow-spacing': [2, {before: true, after: true}], + 'constructor-super': 2, + 'generator-star-spacing': [2, 'both'], + 'no-class-assign': 2, + 'no-const-assign': 2, + 'no-dupe-class-members': 2, + 'no-new-symbol': 2, + 'no-this-before-super': 2, + 'no-useless-constructor': 2, + 'template-curly-spacing': 2, + 'yield-star-spacing': [2, 'both'] + } +} diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..a6fe0b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +data +log +node_modules + +# Built Assets +src/public/index.css diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..5e7ce09 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Todd H. Gardner + +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/README.md b/README.md new file mode 100755 index 0000000..9659fc5 --- /dev/null +++ b/README.md @@ -0,0 +1,65 @@ +# getRANTR + +Social connections are overrated-they only lead to trouble, pain, and lawsuits. Modern social networks have given rise to trolls, fake news, and mansplaining. + +Introducing getRANTR, the revolutionary anti-social distributed network. getRANTR gives you all the obsessive status updating of a social network, with non of the misunderstandings and hurt feelings. With getRANTR, no one can see you RANT. + +## Getting Started + +getRANTR is a distributed network. This means you download the code and run it yourself! No more annoying "public websites" or "cloud infrastructure". getRANTR includes all the fun of running your own technology stack. + +### Software Dependencies + +getRANTR is built on NodeJS. If you don't have it already, you can download the latest runtime from the [NodeJS website](https://nodejs.org/en/). For best results, we recommend using version **4.6.0 or higher**. + +You may also need the following tools to enhance or debug getRANTR: + +- [Atom](https://atom.io/) or another JavaScript-aware text editor +- [Chrome](https://www.google.com/chrome/) web browser +- [Charles](https://www.charlesproxy.com/) or [Fiddler](http://www.telerik.com/fiddler) web proxy + +### Installation + +getRANTR uses [gulp](http://gulpjs.com/) to handle installation and build automation. You'll want to have it installed globally in your **Node** packages. You'll also need to install all the local dependencies, **in the same directory as getRANTR** for running getRANTR: + +``` +npm install gulp --global +npm install +``` + +### Running getRANTR + +After setting everything up and installing the dependencies, you can run getRANTR by: + +``` +npm start +``` + +This will run getRANTR at `http://www.getRANTR.com:9000/` and will setup to automatically reload if any of the source files are changed. + +### Repository Structure + +``` +/build Automation and build tasks +/data Data location for getRANTR users +/exercises Guides and help for workshop exercises +/node_modules There be dragons here +/src Source code + /controllers Server API actions + /public Client-side application (this is probably what you are looking for) +/test Source code tests +``` + +### Impersonating Users + +There are three example users loaded into the system. You can impersonate them with the following commands, after closing the node session (`control` and `C` on Mac): + +``` +node import 1 # Will impersonate Todd's default account +node import 2 # Will impersonate the Keystone user account +node import 3 # Will impersonate Todd's alt account +``` + +Todd's default account is loaded automatically when you install. + +Happy Ranting! diff --git a/build/config/wdio.conf.js b/build/config/wdio.conf.js new file mode 100755 index 0000000..1353c5c --- /dev/null +++ b/build/config/wdio.conf.js @@ -0,0 +1,249 @@ +/* global browser */ + +exports.config = { + + // ===================== + // Server Configurations + // ===================== + // Host address of the running Selenium server. This information is usually obsolete as + // WebdriverIO automatically connects to localhost. Also, if you are using one of the + // supported cloud services like Sauce Labs, Browserstack, or Testing Bot you don't + // need to define host and port information because WebdriverIO can figure that out + // according to your user and key information. However, if you are using a private Selenium + // backend you should define the host address, port, and path here. + // + host: 'localhost', + port: 4444, + path: '/wd/hub', + // + // ================= + // Service Providers + // ================= + // WebdriverIO supports Sauce Labs, Browserstack, and Testing Bot (other cloud providers + // should work too though). These services define specific user and key (or access key) + // values you need to put in here in order to connect to these services. + // + // user: 'webdriverio', + // key: 'xxxxxxxxxxxxxxxx-xxxxxx-xxxxx-xxxxxxxxx', + // + // ================== + // Specify Test Files + // ================== + // Define which test specs should run. The pattern is relative to the directory + // from which `wdio` was called. Notice that, if you are calling `wdio` from an + // NPM script (see https://docs.npmjs.com/cli/run-script) then the current working + // directory is where your package.json resides, so `wdio` will be called from there. + // + specs: [ + 'test/e2e/*.spec.js' + ], + // Patterns to exclude. + // exclude: [ + // 'test/spec/multibrowser/**', + // 'test/spec/mobile/**' + // ], + // + // ============ + // Capabilities + // ============ + // Define your capabilities here. WebdriverIO can run multiple capabilities at the same + // time. Depending on the number of capabilities, WebdriverIO launches several test + // sessions. Within your capabilities you can overwrite the spec and exclude options in + // order to group specific specs to a specific capability. + // + // First, you can define how many instances should be started at the same time. Let's + // say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have + // set maxInstances to 1, wdio will spawn 3 processes. Therefore, if you have 10 spec + // files and you set maxInstances to 10; all spec files will get tested at the same time + // and 30 processes will get spawned. The property handles how many capabilities + // from the same test should run tests. + // + // + // maxInstances: 3, + // + // If you have trouble getting all important capabilities together, check out the + // Sauce Labs platform configurator - a great tool to configure your capabilities: + // https://docs.saucelabs.com/reference/platforms-configurator + // + capabilities: [{ + browserName: 'chrome' + }], + // + // + // + // =================== + // Test Configurations + // =================== + // Define all options that are relevant for the WebdriverIO instance here + // + // By default WebdriverIO commands are executed in a synchronous way using + // the wdio-sync package. If you still want to run your tests in an async way + // e.g. using promises you can set the sync option to false. + sync: false, + // + // Level of logging verbosity: silent | verbose | command | data | result | error + logLevel: 'silent', + // + // Enables colors for log output. + coloredLogs: true, + // + // Saves a screenshot to a given path if a command fails. + screenshotPath: 'log', + // + // Set a base URL in order to shorten url command calls. If your url parameter starts + // with "/", then the base url gets prepended. + baseUrl: 'http://localhost:8080', + // + // Default timeout for all waitFor* commands. + waitforTimeout: 10000, + // + // Initialize the browser instance with a WebdriverIO plugin. The object should have the + // plugin name as key and the desired plugin options as properties. Make sure you have + // the plugin installed before running any tests. The following plugins are currently + // available: + // WebdriverCSS: https://github.com/webdriverio/webdrivercss + // WebdriverRTC: https://github.com/webdriverio/webdriverrtc + // Browserevent: https://github.com/webdriverio/browserevent + plugins: {}, + // + // Framework you want to run your specs with. + // The following are supported: Mocha, Jasmine, and Cucumber + // see also: http://webdriver.io/guide/testrunner/frameworks.html + // + // Make sure you have the wdio adapter package for the specific framework + // installed before running any tests. + framework: 'jasmine', + // + // Test reporter for stdout. + // The only one supported by default is 'dot' + // see also: http://webdriver.io/guide/testrunner/reporters.html + reporters: ['spec'], + // + // Some reporters require additional information which should get defined here + reporterOptions: { + // + // If you are using the "xunit" reporter you should define the directory where + // WebdriverIO should save all unit reports. + outputDir: './' + }, + // + // Options to be passed to Mocha. + // See the full list at http://mochajs.org/ + // mochaOpts: { + // ui: 'bdd' + // }, + // + // Options to be passed to Jasmine. + // See also: https://github.com/webdriverio/wdio-jasmine-framework#jasminenodeopts-options + jasmineNodeOpts: { + // + // Jasmine default timeout + defaultTimeoutInterval: 60000, + // + // The Jasmine framework allows interception of each assertion in order to log the state of the application + // or website depending on the result. For example, it is pretty handy to take a screenshot every time + // an assertion fails. + // expectationResultHandler: function(passed, assertion) { + // // do something + // }, + // + // Make use of Jasmine-specific grep functionality + grep: null, + invertGrep: null + } + // + // If you are using Cucumber you need to specify the location of your step definitions. + // See also: https://github.com/webdriverio/wdio-cucumber-framework#cucumberopts-options + // cucumberOpts: { + // require: [], // (file/dir) require files before executing features + // backtrace: false, // show full backtrace for errors + // compiler: [], // ("extension:module") require files with the given EXTENSION after requiring MODULE (repeatable) + // dryRun: false, // invoke formatters without executing steps + // failFast: false, // abort the run on first failure + // format: ['pretty'], // (type[:path]) specify the output format, optionally supply PATH to redirect formatter output (repeatable) + // colors: true, // disable colors in formatter output + // snippets: true, // hide step definition snippets for pending steps + // source: true, // hide source uris + // profile: [], // (name) specify the profile to use + // require: [], // (file/dir) require files before executing features + // strict: false, // fail if there are any undefined or pending steps + // tags: [], // (expression) only execute the features or scenarios with tags matching the expression + // timeout: 20000, // timeout for step definitions + // ignoreUndefinedDefinitions: false, // Enable this config to treat undefined definitions as warnings. + // }, + // + // ===== + // Hooks + // ===== + // WebdriverIO provides several hooks you can use to interfere with the test process in order to enhance + // it and to build services around it. You can either apply a single function or an array of + // methods to it. If one of them returns with a promise, WebdriverIO will wait until that promise got + // resolved to continue. + // + // Gets executed once before all workers get launched. + // onPrepare: function (config, capabilities) { + // }, + // + // Gets executed before test execution begins. At this point you can access to all global + // variables, such as `browser`. It is the perfect place to define custom commands. + // before: function(capabilities) { + // }, + // + // Hook that gets executed before the suite starts + // beforeSuite: function (suite) { + // }, + // + // Hook that gets executed _before_ a hook within the suite starts (e.g. runs before calling + // beforeEach in Mocha) + // beforeHook: function () { + // }, + // + // Hook that gets executed _after_ a hook within the suite starts (e.g. runs after calling + // afterEach in Mocha) + // afterHook: function () { + // }, + // + // Function to be executed before a test (in Mocha/Jasmine) or a step (in Cucumber) starts. + // beforeTest: function (test) { + // }, + // + // Runs before a WebdriverIO command gets executed. + // beforeCommand: function (commandName, args) { + // }, + // + // Runs after a WebdriverIO command gets executed + // afterCommand: function (commandName, args, result, error) { + // }, + // + // Function to be executed after a test (in Mocha/Jasmine) or a step (in Cucumber) starts. + // afterTest: function (test) { + // }, + // + // Hook that gets executed after the suite has ended + // afterSuite: function (suite) { + // }, + // + // Gets executed after all tests are done. You still have access to all global variables from + // the test. + // after: function (result, capabilities, specs) { + // }, + // + // Gets executed after all workers got shut down and the process is about to exit. It is not + // possible to defer the end of the process using a promise. + // onComplete: function (exitCode) { + // }, + // + // Cucumber specific hooks + // beforeFeature: function (feature) { + // }, + // beforeScenario: function (scenario) { + // }, + // beforeStep: function (step) { + // }, + // afterStep: function (stepResult) { + // }, + // afterScenario: function (scenario) { + // }, + // afterFeature: function (feature) { + // } +}; diff --git a/build/tasks/build.js b/build/tasks/build.js new file mode 100755 index 0000000..74b2f8f --- /dev/null +++ b/build/tasks/build.js @@ -0,0 +1,12 @@ +var gulp = require('gulp'); +var sass = require('gulp-sass'); + +gulp.task('build', ['js', 'sass']); + +gulp.task('js', () => {}); + +gulp.task('sass', () => { + return gulp.src('./src/public/styles/index.scss') + .pipe(sass({ outputStyle: 'compressed' }).on('error', sass.logError)) + .pipe(gulp.dest('./src/public/')); +}); diff --git a/build/tasks/test.js b/build/tasks/test.js new file mode 100755 index 0000000..9930219 --- /dev/null +++ b/build/tasks/test.js @@ -0,0 +1,48 @@ +var gulp = require('gulp'); +var gutil = require('gulp-util'); +var webdriver = require('gulp-webdriver'); +var mkdirp = require('mkdirp'); +var path = require('path'); +var selenium = require('selenium-standalone'); + +var BASE_PATH = path.resolve(__dirname, '../..'); + +gulp.task('test', ['build', 'test:e2e']); + +gulp.task('selenium', (done) => { + mkdirp(BASE_PATH + '/log', (err) => { + if (err) { gutil.log(gutil.colors.red(err)); return done(err); } + var seleniumConfig = { + version: '3.0.1', + acceptSslCerts: true, + baseURL: 'https://selenium-release.storage.googleapis.com', + drivers: { + chrome: { + version: '2.24', + arch: process.arch, + baseURL: 'https://chromedriver.storage.googleapis.com' + } + } + }; + selenium.install(seleniumConfig, (err) => { + if (err) { gutil.log(gutil.colors.red(err)); return done(err); } + selenium.start(seleniumConfig, (err, child) => { + if (err) { gutil.log(gutil.colors.red(err)); return done(err); } + selenium._process = child; + done(); + }); + }); + }); +}); + +gulp.task('test:e2e', ['test-server', 'selenium'], () => { + return gulp.src(BASE_PATH + '/build/config/wdio.conf.js') + .pipe(webdriver()) + .on('end', () => { selenium._process.kill(); process.exit(); }) + .on('error', (err) => { gutil.log(gutil.colors.red(err)); selenium._process.kill(); throw err; }); +}); + +gulp.task('test-server', (done) => { + require(BASE_PATH + '/src/index.js'); + done(); +}); diff --git a/exercises/1_delete_rant.md b/exercises/1_delete_rant.md new file mode 100755 index 0000000..f5ad122 --- /dev/null +++ b/exercises/1_delete_rant.md @@ -0,0 +1,66 @@ +Exercise 1 - Delete Rant +================ + +# Goal +User should be able to delete a rant from their timeline using the "delete" control. + + +# Evidence +- User cannot delete a rant from their timeline. + + +# Concepts +- Using Keystone Users (`node import 2`) +- [Setting up Workspaces](https://developers.google.com/web/tools/setup/setup-workflow) + - Chrome > Sources > Context Menu > Add Folder to Workspace + - Select the getRANTR/src/public Folder + - Allow Chrome filesystem access + - Chrome > Sources > Context Menu > Map to File System Resource +- Chrome "Persist Log" +- Chrome DOM Event Listeners +- [Chrome Async Stack traces](https://www.html5rocks.com/en/tutorials/developertools/async-call-stack/) + + + + + + + + + + + +# Spoilers (Try Yourself First) + +## Key Changes + +- `/src/public/scripts/rantListView.js:initialize()` Missing `preventDefault` on event + +``` +initialize: function() { + this.model.on('change', this.render, this); + this.$el.on('click', function(evt) { + if (evt.target.matches('.js-delete')) { + evt.preventDefault(); // <-- Add This! + this.onDelete(evt); + } + }.bind(this)); +}, +``` + +- `/src/public/scripts/rantListView.js:onDelete()` Loss of function context (this). + +``` +// Many possible solutions +onDelete: function() { + setTimeout(function() { + this.model.destroy(); + this.remove(); + }.bind(this)); // <-- Add .bind(this) +} +``` + +## More Information + +- [Event PreventDefault](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault) +- [Function Context](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this) diff --git a/exercises/2_clear_rant_text.md b/exercises/2_clear_rant_text.md new file mode 100755 index 0000000..703e70f --- /dev/null +++ b/exercises/2_clear_rant_text.md @@ -0,0 +1,43 @@ +Exercise 2 - Clear Rant Text +================ + +# Goal +Page should clear draft rant text when cleared. + + +# Evidence +- User enters some text into the rant box. +- User select-all, and deletes the text +- User reloads the page, see original text in the rant box. + + +# Concepts +- Chrome DOM Event Listeners +- Chrome Application Storage +- Chrome Source debugger and stepping + + + + + + + + +# Spoilers (Try Yourself First) + +## Key Files + +- `/src/public/scripts/addRantView.js:onChange()` Empty text value will be falsy + +``` +onChange: function(evt) { + var text = (evt.target || {}).value; + if (typeof text === 'string') { // <-- Change this to check for string + localStorage.setItem('next-rant', text); + } +}, +``` + +## Solution Links + +- [Falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) diff --git a/exercises/3_400_Response.md b/exercises/3_400_Response.md new file mode 100755 index 0000000..6ba40be --- /dev/null +++ b/exercises/3_400_Response.md @@ -0,0 +1,67 @@ +Exercise 3 - 400 Server Responses +================ + +# Goal +Identify cause and resolve 400's responses from the server. + + +# Evidence +- Monitoring reporting large numbers of 400 responses from `POST /api/rants` +- User enters 0 characters into textarea#rant_text +- User clicks submit button + + +# Concepts +- Chrome Network Inspector and Preview +- Chrome Source debugger and stepping + + + + + + + + + + + + +# Spoilers (Try Yourself First) + +## Key Files + +- `/src/public/scripts/addRantView.js:onSubmit()` Should check for valid input + +``` +onSubmit: function(evt) { + evt.preventDefault(); + var form = evt.target; + var rant = { + text: form.rant.value + }; + + if (rant.text && rant.text.length > 0) { // <-- add this validation check + analytics.trackConversion(); + this.collection.create(rant, { wait: true }); + + form.rant.value = ''; + } +} +``` + +- `/src/public/index.html` textarea should have required attribute + +``` +
+ + + +
+ +
+
+``` + +## Solution Links + +- [Input:Required](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) diff --git a/exercises/4_Substr_of_Null.md b/exercises/4_Substr_of_Null.md new file mode 100755 index 0000000..7ceeb54 --- /dev/null +++ b/exercises/4_Substr_of_Null.md @@ -0,0 +1,44 @@ +Exercise 4 - Substr of Null +================ + +# Goal +Why does a single user get `cannot read substr of null` errors? + + +# Evidence +- Using "Alt Todd" Account (`node import 3`) +- UI is not fully shown +- Error visible in console + + +# Concepts +- Chrome Network Inspector and Preview +- Chrome Source debugger and stepping + + + + + + + + +# Spoilers (Try Yourself First) + +## Key Files + +- `/src/public/scripts/rantListView.js:render()` Should check for valid rant before rendering + +``` +render: function() { + var rant = this.model.toJSON(); + if (typeof rant.text !== 'string') { // <-- add this validation check + console.error('invalid rant data', rant); + rant.text = 'Sorry! An error occurred with this rant. Our team is on it!'; + } + + this.$el.html(this.template(rant)); + return this; +}, +``` + +## Solution Links diff --git a/exercises/5_Memory_Leak.md b/exercises/5_Memory_Leak.md new file mode 100755 index 0000000..63bec39 --- /dev/null +++ b/exercises/5_Memory_Leak.md @@ -0,0 +1,44 @@ +Exercise 5 - Memory Leak +================ + +# Goal +Locate the source of the largest uncollected memory allocations in getRANTR + + +# Evidence +- Using "Keystone" Account (`node import 2`) +- Users report slowing performance +- Conduct a memory profile (for at least 30 seconds) + + +# Concepts +- [Chrome Allocation Snapshots](https://developers.google.com/web/tools/chrome-devtools/memory-problems/allocation-profiler) +- [Chrome Timeline](https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/timeline-tool) +- Timeline with JS Profile and Memory checked + + + + + + +# Spoilers (Try Yourself First) + +## Key Files + +- `/src/public/scripts/adListView.js:render()` Clears container node without destroying children. + +``` +render: function() { + this.el.innerHTML = ''; + this.children.forEach((view) => { view.remove(); }); // <-- optional, cleanup each child + this.children.length = 0; // <-- release children for garbage cleanup + this.collection.each((model) => { + this.renderAd(model); + }); + return this; +}, +``` + +## Solution Links + +- [Chrome Memory Debugging](https://developers.google.com/web/tools/chrome-devtools/memory-problems/) diff --git a/exercises/6_Load_Performance.md b/exercises/6_Load_Performance.md new file mode 100755 index 0000000..9ba3db4 --- /dev/null +++ b/exercises/6_Load_Performance.md @@ -0,0 +1,37 @@ +Exercise 6 - Load Performance +================ + +# Goal +Locate 3 sources with impact on load performance + + +# Evidence +- Users report the site is slow to load. +- Simulate with Network Throttle: Chrome > Network > Throttle > Good 3G +- Run Timeline: Network, Screenshots, Memory + + +# Concepts +- [Chrome Network Throttle](https://developers.google.com/web/tools/chrome-devtools/network-performance/network-conditions) +- [Chrome Timeline](https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/timeline-tool) +- Timeline with Network, Screenshots, and Memory checked + + + + + + + + +# Spoilers (Try Yourself First) + +## Key Indicators + +- `/src/public/index.html` Blocks for fonts from Google API +- `/src/public/index.html` Loads lots of scripts independently +- `/src/public/scripts/*` Are not compressed or cached +- `/src/public/images/*` Are way to big for what they need to be + +## Solution Links + +- [Tracking Real World Performance](https://vimeo.com/113715672) diff --git a/exercises/7_Third_Party.md b/exercises/7_Third_Party.md new file mode 100755 index 0000000..c741a7f --- /dev/null +++ b/exercises/7_Third_Party.md @@ -0,0 +1,39 @@ +Exercise 7 - Third Party +================ + +# Goal +Why did we get "form.submit is not a function" suddenly? + + +# Evidence +- (Remember to remove the previous network throttle) +- Signup form on `http://www.getrantr.com:9000/signup.html` is broken. +- Code from remove vendor script breaking form submit. +- Fix issue *WITHOUT* changing anything in vendor/analytics.js + + +# Concepts +- Third party scripts. + + + + + + + + +# Spoilers (Try Yourself First) + +## Key Files + +- `/src/public/scripts/analytics.js:submit listener()` Expects form.submit to always be a function, which is not the case if there is an input with name=submit. Change the form on `signup.html` to not have such an input. + +``` + +``` + +## Solution Links + +- [When Form.submit is not a Function](https://trackjs.com/blog/when-form-submit-is-not-a-function/) diff --git a/exercises/8_Network_Not_Defined.md b/exercises/8_Network_Not_Defined.md new file mode 100755 index 0000000..0c98417 --- /dev/null +++ b/exercises/8_Network_Not_Defined.md @@ -0,0 +1,39 @@ +Exercise 8 - Network is Not Defined +================ + +# Goal +Notify monitoring and user in case of major dependency failure. + + +# Evidence +- analytics and $ (jQuery) will sometimes not be defined based on monitoring +- Comment out the jQuery script, how does the UI appear +- Prove by simulating flaky networks with Charles or Fiddler + + +# Concepts +- [Progressive Enhancement](http://alistapart.com/article/understandingprogressiveenhancement) +- Failure fallbacks + + + + + + + + + +# Spoilers (Try Yourself First) + +## Key Files + +- `/src/public/scripts/index.js` Main listener. Check for existence and show warning. + +``` +if (!$) { + console.error('Failed to load jQuery dependency.'); + document.body.innerHTML = '

Sorry! An error occurred loading the application. Please try again.

'; +} +``` + +## Solution Links diff --git a/exercises/charles/CharlesRewrite-AnalyticsFail.xml b/exercises/charles/CharlesRewrite-AnalyticsFail.xml new file mode 100755 index 0000000..c0cbf05 --- /dev/null +++ b/exercises/charles/CharlesRewrite-AnalyticsFail.xml @@ -0,0 +1,53 @@ + + + + + true + getRANTr - Analytics Fails + + + + + http + cdn.getrantr.com + 9000 + /scripts/analytics.js + + true + + + + + + true + 11 + 200 + false + false + false + false + 404 + false + false + false + false + 2 + + + true + 7 + + false + false + false + true + + false + false + false + false + 2 + + + + diff --git a/exercises/charles/CharlesThrottle-3GSubway.xml b/exercises/charles/CharlesThrottle-3GSubway.xml new file mode 100755 index 0000000..f6d8fda --- /dev/null +++ b/exercises/charles/CharlesThrottle-3GSubway.xml @@ -0,0 +1,33 @@ + + + + 16384.0 + 8192.0 + 100 + 100 + 500 + 99 + 576 + 100 + 100 + 99 + + + 16384.0 + 8192.0 + 100 + 100 + 500 + 576 + 99 + 99 + 100 + 100 + 3G on the Subway + + + + + + false + \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js new file mode 100755 index 0000000..7a202e3 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,16 @@ +var gulp = require('gulp'); +var nodemon = require('gulp-nodemon'); +var fs = require('fs'); + +fs.readdirSync('./build/tasks').forEach(function(file) { + require('./build/tasks/' + file); +}); + +gulp.task('start', ['build'], function() { + nodemon({ + script: 'src/index.js', + tasks: ['build'], + ext: 'js html scss', + env: { 'NODE_ENV': 'development' } + }); +}); diff --git a/import.js b/import.js new file mode 100755 index 0000000..e48be07 --- /dev/null +++ b/import.js @@ -0,0 +1,366 @@ +var Datastore = require('nedb'); + +var db = {}; +db.ads = new Datastore({ filename: './data/ads.db', autoload: true }); +db.users = new Datastore({ filename: './data/users.db', autoload: true }); +db.rants = new Datastore({ filename: './data/rants.db', autoload: true }); + +switch (process.argv[2]) { + case '1': + case 'todd': + importTodd(db, () => { + console.log('Imported User 1 (Todd)'); + }); + break; + + case '2': + case 'donald': + importDonald(db, () => { + console.log('Imported User 2 (Donald)'); + }); + break; + + case '3': + case 'alttodd': + importAltTodd(db, () => { + console.log('Imported User 3 (Alt Todd)'); + }); + break; + + default: + console.warn(`Invalid import ${process.argv[2]}`); + +} + +function importTodd(db, callback) { + var user = { + name: 'Todd H. Gardner', + imageURL: '/images/profile1.jpg', + joinedOn: '2015-06-01T00:00:00.000Z' + }; + + var rants = []; + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'There are only two hard problems in programming: cache invalidation, and being willing to relocate to San Francisco', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 2).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'The year is 2017. Donald J. Trump is President. The primary resistance leaders are Teen Vogue and the Merriam-Webster online dictionary.', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 4).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'That thing where you buy some domains for a crazy idea...', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 6).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'If body { background: red; } isn’t the first thing you do to test a CSS file is included, then I don’t know what kind of developer you are.', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 10).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'How did health insurance pull the scam of counting eyeballs and mouths as not part of the body and not eligible for normal health care?', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 16).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'JavaScript sneaks into all sorts of places it shouldn\'t be because its the easiest way to do it on a tight timeline.', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 20).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: '*STEFON VOICE* This release has everything:\n- features\n- bug fixes\n- a root kit from a shadowy government agency\n- and human load balancers', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 26).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'People are the lifeblood of this organisation and we respect each employee as an individual.\n\nDoes that answer your question, number 795?', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 28).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'Today I learned "Standards are like toothbrushes: Everyone wants one but no one wants to use someone else\'s"', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 32).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'While we\'re at it, let\'s cut their budget to a third and redirect the funds to academic and ethical studies.', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 38).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'Business have to lose so much money to software resellers--I don\'t understand why this idea exists anymore.', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 48).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'McDonalds Breakfast is literally the only reason to go to McDonalds.', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 56).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'Startup (noun): Company running on other people\'s money, with a product running on other people\'s computers, selling other people\'s data.', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 60).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'just when you think you finally have a hold on IE, Safari comes and slams you with a chair across the back.', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 60).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'Hey, @MSEdgeDev I have a reproduceable IE11 Freezing bug, but all I can find is the Edge tracker. Should I report it there?', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 72).toISOString() + }); + + var ads = []; + ads.push({ + advertiserId: 'REPLACE_ME', + imageURL: '/images/ad-bacon.jpg', + targetURL: 'http://www.baconfreak.com/' + }); + ads.push({ + advertiserId: 'REPLACE_ME', + imageURL: '/images/ad-dundee.png', + targetURL: 'https://www.youtube.com/watch?v=POJtaO2xB_o' + }); + ads.push({ + advertiserId: 'REPLACE_ME', + imageURL: '/images/ad-bear.jpeg', + targetURL: 'http://www.bear.org/website/' + }); + ads.push({ + advertiserId: 'REPLACE_ME', + imageURL: '/images/ad-hasslehoff.png', + targetURL: 'http://davidhasselhoffonline.com/bear' + }); + + db.rants.remove({}, { multi: true }, () => { + db.rants.insert(rants, () => { + db.ads.remove({}, { multi: true }, () => { + db.ads.insert(ads, () => { + db.users.remove({}, { multi: true }, () => { + db.users.insert(user, callback); + }); + }); + }); + }); + }); +} + +function importDonald(db, callback) { + var user = { + name: 'Donald J. Trump', + imageURL: '/images/profile2.jpg', + joinedOn: '2009-03-01T00:00:00.000Z' + }; + + var rants = []; + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'What is our country coming to when a judge can halt a Homeland Security travel ban and anyone, even with bad intentions, can come into U.S.?', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 2).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'MAKE AMERICA GREAT AGAIN!', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 4).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'The opinion of this so-called judge, which essentially takes law-enforcement away from our country, is ridiculous and will be overturned!', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 6).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: '.@CNN is in a total meltdown with their FAKE NEWS because their ratings are tanking since election and their credibility will soon be gone!', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 10).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'We had a great News Conference at Trump Tower today. A couple of FAKE NEWS organizations were there but the people truly get what\'s going on', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 16).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'Intelligence agencies should never have allowed this fake news to "leak" into the public. One last shot at me.Are we living in Nazi Germany?', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 20).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'I win an election easily, a great "movement" is verified, and crooked opponents try to belittle our victory with FAKE NEWS. A sorry state!', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 26).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'FAKE NEWS - A TOTAL POLITICAL WITCH HUNT!', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 28).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: '.@ariannahuff is unattractive both inside and out. I fully understand why her former husband left her for a man- he made a good decision.', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 32).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'Sorry losers and haters, but my I.Q. is one of the highest -and you all know it! Please don\'t feel so stupid or insecure,it\'s not your fault', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 38).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'Barney Frank looked disgusting--nipples protruding--in his blue shirt before Congress. Very very disrespectful.', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 48).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'Dopey Prince @Alwaleed_Talal wants to control our U.S. politicians with daddy’s money. Can’t do it when I get elected. #Trump2016', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 56).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'Rosie is crude, rude, obnoxious and dumb - other than that I like her very much!', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 60).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'Hillary Clinton has announced that she is letting her husband out to campaign but HE\'S DEMONSTRATED A PENCHANT FOR SEXISM, so inappropriate!', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 60).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: 'The concept of global warming was created by and for the Chinese in order to make U.S. manufacturing non-competitive.', + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 72).toISOString() + }); + + var ads = []; + ads.push({ + advertiserId: 'REPLACE_ME', + imageURL: '/images/ad-russia.jpg', + targetURL: 'https://www.travelallrussia.com/' + }); + ads.push({ + advertiserId: 'REPLACE_ME', + imageURL: '/images/ad-goldbathroom.jpg', + targetURL: 'http://www.houzz.com/gold-bathroom' + }); + ads.push({ + advertiserId: 'REPLACE_ME', + imageURL: '/images/ad-hair.jpg', + targetURL: 'http://www.hairclub.com/' + }); + + db.rants.remove({}, { multi: true }, () => { + db.rants.insert(rants, () => { + db.ads.remove({}, { multi: true }, () => { + db.ads.insert(ads, () => { + db.users.remove({}, { multi: true }, () => { + db.users.insert(user, callback); + }); + }); + }); + }); + }); + +} + +function importAltTodd(db, callback) { + var user = { + name: 'AltTodd', + imageURL: '/images/profile1a.jpg', + joinedOn: null + }; + + var rants = []; + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: null, + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 2).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: null, + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 4).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: null, + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 6).toISOString() + }); + rants.push({ + name: user.name, + imageURL: user.imageURL, + text: null, + timestamp: new Date((new Date()) * 1 - 1000 * 3600 * 10).toISOString() + }); + + var ads = []; + ads.push({ + advertiserId: 'REPLACE_ME', + imageURL: '/images/ad-bacon.jpg', + targetURL: 'http://www.baconfreak.com/' + }); + ads.push({ + advertiserId: 'REPLACE_ME', + imageURL: '/images/ad-dundee.png', + targetURL: 'https://www.youtube.com/watch?v=POJtaO2xB_o' + }); + ads.push({ + advertiserId: 'REPLACE_ME', + imageURL: '/images/ad-bear.jpeg', + targetURL: 'http://www.bear.org/website/' + }); + ads.push({ + advertiserId: 'REPLACE_ME', + imageURL: '/images/ad-hasslehoff.png', + targetURL: 'http://davidhasselhoffonline.com/bear' + }); + + db.rants.remove({}, { multi: true }, () => { + db.rants.insert(rants, () => { + db.ads.remove({}, { multi: true }, () => { + db.ads.insert(ads, () => { + db.users.remove({}, { multi: true }, () => { + db.users.insert(user, callback); + }); + }); + }); + }); + }); + +} diff --git a/package.json b/package.json new file mode 100755 index 0000000..904591c --- /dev/null +++ b/package.json @@ -0,0 +1,40 @@ +{ + "name": "getRANTr", + "version": "2.0.0", + "description": "JavaScript Debugging Playground", + "main": "public/index.html", + "scripts": { + "postinstall": "node import todd", + "start": "gulp start", + "test": "gulp test" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/toddhgardner/soliloquy2.git" + }, + "author": "Todd H Gardner ", + "license": "MIT", + "bugs": { + "url": "https://github.com/toddhgardner/getRANTr/issues" + }, + "homepage": "https://github.com/toddhgardner/getRANTr#readme", + "dependencies": { + "body-parser": "1.15.2", + "express": "4.14.0", + "nedb": "1.8.0" + }, + "devDependencies": { + "dns": "0.2.2", + "gulp": "3.9.1", + "gulp-include": "2.3.1", + "gulp-nodemon": "2.2.1", + "gulp-sass": "3.1.0", + "gulp-util": "3.0.8", + "gulp-webdriver": "2.0.3", + "mkdirp": "0.5.1", + "selenium-standalone": "6.0.1", + "wdio-jasmine-framework": "0.2.19", + "wdio-spec-reporter": "0.0.5", + "webdriverio": "4.6.2" + } +} diff --git a/src/controllers/adsController.js b/src/controllers/adsController.js new file mode 100755 index 0000000..8350468 --- /dev/null +++ b/src/controllers/adsController.js @@ -0,0 +1,45 @@ + +var ID_CHARS = '23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; +function getAdvertiserId() { + var res = ''; + for (var i = 0; i < 5000; i++) { + res += ID_CHARS.charAt(Math.floor(Math.random() * ID_CHARS.length)); + } + return res; +} + +function shuffle(array) { + var currentIndex = array.length, temporaryValue, randomIndex; + + // While there remain elements to shuffle... + while (0 !== currentIndex) { + + // Pick a remaining element... + randomIndex = Math.floor(Math.random() * currentIndex); + currentIndex -= 1; + + // And swap it with the current element. + temporaryValue = array[currentIndex]; + array[currentIndex] = array[randomIndex]; + array[randomIndex] = temporaryValue; + } + + return array; +} + +module.exports = (app, db) => { + + app.get('/api/ads', (req, res, next) => { + db.ads + .find({}) + .exec((err, docs) => { + for (var i = 0; i < docs.length; i++) { + docs[i].advertiserId = getAdvertiserId(); + } + var ads = shuffle(docs); + res.json(ads); + next(); + }); + }); + +}; diff --git a/src/controllers/analyticsController.js b/src/controllers/analyticsController.js new file mode 100755 index 0000000..cd92d2b --- /dev/null +++ b/src/controllers/analyticsController.js @@ -0,0 +1,9 @@ + +module.exports = (app, db) => { + + app.get('/api/analytics/*', (req, res, next) => { + res.json({}); + next(); + }); + +}; diff --git a/src/controllers/rantsController.js b/src/controllers/rantsController.js new file mode 100755 index 0000000..95b16b5 --- /dev/null +++ b/src/controllers/rantsController.js @@ -0,0 +1,48 @@ +var bodyParser = require('body-parser'); + +var jsonParser = bodyParser.json(); +var baseURL = '/api/rants'; + +module.exports = (app, db) => { + + app.get(baseURL, function(req, res, next) { + db.rants + .find({}) + .sort({ timestamp: 1 }) + .exec((err, docs) => { + res.json(docs); + next(); + }); + }); + + app.post(baseURL, jsonParser, (req, res, next) => { + var text = (req.body || {}).text; + if (!text) { + res.status(400).json({ message: 'text must not be empty' }); + return next(); + } + db.users + .find({}) + .limit(1) + .exec((err, user) => { + var rant = { + text: text, + timestamp: new Date().toISOString(), + name: user[0].name, + imageURL: user[0].imageURL + }; + db.rants.insert(rant, (err, saved) => { + res.json(saved); + next(); + }); + }); + }); + + app.delete(baseURL + '/:id', (req, res, next) => { + db.rants.remove({ _id: req.params.id }, (err, removed) => { + res.json({}); + next(); + }); + }); + +}; diff --git a/src/controllers/usersController.js b/src/controllers/usersController.js new file mode 100755 index 0000000..93ccbd5 --- /dev/null +++ b/src/controllers/usersController.js @@ -0,0 +1,13 @@ +module.exports = (app, db) => { + + app.get('/api/user', function(req, res, next) { + db.users + .find({}) + .sort({ timestamp: 1 }) + .exec((err, docs) => { + res.json(docs[0]); + next(); + }); + }); + +}; diff --git a/src/index.js b/src/index.js new file mode 100755 index 0000000..7985b28 --- /dev/null +++ b/src/index.js @@ -0,0 +1,22 @@ +var Datastore = require('nedb'); +var express = require('express'); + +var app = express(); +var db = {}; +db.ads = new Datastore({ filename: './data/ads.db', autoload: true }); +db.users = new Datastore({ filename: './data/users.db', autoload: true }); +db.rants = new Datastore({ filename: './data/rants.db', autoload: true }); + +// app.use(function(req, res, next) { +// res.header('Access-Control-Allow-Origin', 'http://www.getrantr.com:9000'); +// next(); +// }); + +require('./controllers/adsController.js')(app, db); +require('./controllers/analyticsController.js')(app, db); +require('./controllers/rantsController.js')(app, db); +require('./controllers/usersController.js')(app, db); + +app.use('/', express.static(__dirname + '/public/')); + +app.listen(9000); diff --git a/src/public/.eslintrc b/src/public/.eslintrc new file mode 100755 index 0000000..901dc09 --- /dev/null +++ b/src/public/.eslintrc @@ -0,0 +1,7 @@ +{ + "globals": { + "$": true, + "Backbone": true, + "moment": true + } +} diff --git a/src/public/images/ad-bacon.jpg b/src/public/images/ad-bacon.jpg new file mode 100755 index 0000000..6e7df62 Binary files /dev/null and b/src/public/images/ad-bacon.jpg differ diff --git a/src/public/images/ad-bear.jpeg b/src/public/images/ad-bear.jpeg new file mode 100755 index 0000000..e594cb5 Binary files /dev/null and b/src/public/images/ad-bear.jpeg differ diff --git a/src/public/images/ad-dundee.png b/src/public/images/ad-dundee.png new file mode 100755 index 0000000..015680e Binary files /dev/null and b/src/public/images/ad-dundee.png differ diff --git a/src/public/images/ad-goldbathroom.jpg b/src/public/images/ad-goldbathroom.jpg new file mode 100755 index 0000000..656b834 Binary files /dev/null and b/src/public/images/ad-goldbathroom.jpg differ diff --git a/src/public/images/ad-hair.jpg b/src/public/images/ad-hair.jpg new file mode 100755 index 0000000..ad7dff0 Binary files /dev/null and b/src/public/images/ad-hair.jpg differ diff --git a/src/public/images/ad-hasslehoff.png b/src/public/images/ad-hasslehoff.png new file mode 100755 index 0000000..4712870 Binary files /dev/null and b/src/public/images/ad-hasslehoff.png differ diff --git a/src/public/images/ad-russia.jpg b/src/public/images/ad-russia.jpg new file mode 100755 index 0000000..5a85783 Binary files /dev/null and b/src/public/images/ad-russia.jpg differ diff --git a/src/public/images/profile1.jpg b/src/public/images/profile1.jpg new file mode 100755 index 0000000..a4b7a48 Binary files /dev/null and b/src/public/images/profile1.jpg differ diff --git a/src/public/images/profile1a.jpg b/src/public/images/profile1a.jpg new file mode 100755 index 0000000..1602ea7 Binary files /dev/null and b/src/public/images/profile1a.jpg differ diff --git a/src/public/images/profile2.jpg b/src/public/images/profile2.jpg new file mode 100755 index 0000000..6f19a08 Binary files /dev/null and b/src/public/images/profile2.jpg differ diff --git a/src/public/index.html b/src/public/index.html new file mode 100755 index 0000000..e5322b3 --- /dev/null +++ b/src/public/index.html @@ -0,0 +1,63 @@ + + + + + getRANTr + + + + + + +
+
+

getRANTr – Where it's all about you

+
+
+ +
+
+
+ +
+
+
+ + +
+ +
+
+
+
+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/public/scripts/adListView.js b/src/public/scripts/adListView.js new file mode 100755 index 0000000..a85ea76 --- /dev/null +++ b/src/public/scripts/adListView.js @@ -0,0 +1,59 @@ +var AdView = Backbone.View.extend({ + + template: function(ad) { + return `
+ + + +
`; + }, + + render: function() { + this.$el.html(this.template(this.model.toJSON())); + return this; + } + +}); + +var AdListView = Backbone.View.extend({ + + children: [], + + initialize: function() { + this.collection.on('reset', this.render, this); + this.collection.on('add', this.renderAd, this); + this.$el.on('click', function(evt) { + var clicked = evt.target.parentElement; + if (clicked.matches('.ad-link')) { + recordClickAnalytics(clicked.getAttribute('data-advertiserId')); + } + }); + // start ad rotation + this.collection.fetch(); + setInterval(function() { + this.collection.fetch({ reset: true }); + }.bind(this), 10000); + }, + + render: function() { + this.el.innerHTML = ''; + this.collection.each((model) => { + this.renderAd(model); + }); + return this; + }, + + renderAd: function(ad) { + var view = new AdView({ + model: ad + }); + this.$el.prepend(view.render().$el); + this.children.push(view); + } + +}); + + +function recordClickAnalytics() { + console.log('someone clicked on an ad!!'); +} diff --git a/src/public/scripts/adModel.js b/src/public/scripts/adModel.js new file mode 100755 index 0000000..cf19f22 --- /dev/null +++ b/src/public/scripts/adModel.js @@ -0,0 +1,8 @@ +var AdModel = Backbone.Model.extend({ + idAttribute: '_id' +}); + +var AdCollection = Backbone.Collection.extend({ + url: '/api/ads/', + model: AdModel +}); diff --git a/src/public/scripts/addRantView.js b/src/public/scripts/addRantView.js new file mode 100755 index 0000000..448e99a --- /dev/null +++ b/src/public/scripts/addRantView.js @@ -0,0 +1,32 @@ +var AddRantView = Backbone.View.extend({ + + initialize: function() { + this.$('textarea').on('keyup', this.onChange.bind(this)); + this.$('form').on('submit', this.onSubmit.bind(this)); + var text = localStorage.getItem('next-rant'); + if (text) { + this.$('textarea').val(text); + } + }, + + onChange: function(evt) { + var text = (evt.target || {}).value; + if (text) { + localStorage.setItem('next-rant', text); + } + }, + + onSubmit: function(evt) { + evt.preventDefault(); + var form = evt.target; + var rant = { + text: form.rant.value + }; + + analytics.trackConversion(); + this.collection.create(rant, { wait: true }); + + form.rant.value = ''; + } + +}); diff --git a/src/public/scripts/analytics.js b/src/public/scripts/analytics.js new file mode 100755 index 0000000..bb361c5 --- /dev/null +++ b/src/public/scripts/analytics.js @@ -0,0 +1,36 @@ +/* global window, XMLHttpRequest */ + +(function() { + 'use strict'; + + function trackConversion(callback) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', 'http://www.getrantr.com:9000/api/analytics/conversion'); + xhr.addEventListener('readystatechange', function() { + if (xhr.readyState === 4) { + if (callback) callback(); + } + }); + xhr.send(); + } + + // New Feature! Now we can automatically Intercept all form submissions as a conversion event. + window.addEventListener('submit', function(evt) { + var form = evt.target; + if (form.hasAttribute('ignore')) { return; } + + evt.preventDefault(); + + trackConversion(function() { + form.submit(); + }); + + }, true); + + window.analytics = { + + trackConversion: trackConversion + + }; + +})(); diff --git a/src/public/scripts/index.js b/src/public/scripts/index.js new file mode 100755 index 0000000..4e794ba --- /dev/null +++ b/src/public/scripts/index.js @@ -0,0 +1,35 @@ +/* global AdCollection, AdListView, AddRantView, RantCollection, RantListView, UserModel, UserView */ + +$(function() { + 'use strict'; + + console.log('I feel a rant coming on.'); + + var ads = new AdCollection(); + var rants = new RantCollection(); + rants.user = new UserModel(); + + var adList = new AdListView({ + el: $('#ads'), + collection: ads + }); + + var userView = new UserView({ + el: $('#user'), + model: rants.user + }); + + var addRant = new AddRantView({ + el: $('#add-rant'), + collection: rants + }); + + var timeline = new RantListView({ + el: $('#timeline'), + collection: rants + }); + + rants.user.fetch(); + rants.fetch(); + +}); diff --git a/src/public/scripts/rantListView.js b/src/public/scripts/rantListView.js new file mode 100755 index 0000000..90fd006 --- /dev/null +++ b/src/public/scripts/rantListView.js @@ -0,0 +1,58 @@ +var RantView = Backbone.View.extend({ + + template: function(rant) { + return `
+ ${rant.name} +
+
${rant.name}
+
${rant.text.substr(0, 140)}
+
+
+
+
${moment(rant.timestamp).fromNow()}
+
+
`; + }, + + initialize: function() { + this.model.on('change', this.render, this); + this.$el.on('click', function(evt) { + if (evt.target.matches('.js-delete')) { + this.onDelete(evt); + } + }.bind(this)); + }, + + render: function() { + this.$el.html(this.template(this.model.toJSON())); + return this; + }, + + onDelete: function() { + setTimeout(function() { + this.model.destroy(); + this.remove(); + }); + } + +}); + +var RantListView = Backbone.View.extend({ + + initialize: function() { + this.collection.on('reset', this.render, this); + this.collection.on('add', this.renderStatement, this); + }, + + render: function() { + this.$el.html(''); + this.collection.each(this.renderStatement, this); + return this; + }, + + renderStatement: function(model) { + var view = new RantView({ model: model }); + this.$el.prepend(view.render().$el); + } + +}); diff --git a/src/public/scripts/rantModel.js b/src/public/scripts/rantModel.js new file mode 100755 index 0000000..0d01dd4 --- /dev/null +++ b/src/public/scripts/rantModel.js @@ -0,0 +1,9 @@ +var RantModel = Backbone.Model.extend({ + idAttribute: '_id' +}); + +var RantCollection = Backbone.Collection.extend({ + url: '/api/rants/', + comparator: 'timestamp', + model: RantModel +}); diff --git a/src/public/scripts/userModel.js b/src/public/scripts/userModel.js new file mode 100755 index 0000000..64265fd --- /dev/null +++ b/src/public/scripts/userModel.js @@ -0,0 +1,4 @@ +var UserModel = Backbone.Model.extend({ + idAttribute: '_id', + url: '/api/user/' +}); diff --git a/src/public/scripts/userView.js b/src/public/scripts/userView.js new file mode 100755 index 0000000..f58effa --- /dev/null +++ b/src/public/scripts/userView.js @@ -0,0 +1,20 @@ +var UserView = Backbone.View.extend({ + + template: function(user) { + return `
+ ${user.name} +
Ranting as
${user.name}
+
Since ${moment(user.joinedOn).format('MMMM YYYY')}
+
`; + }, + + initialize: function() { + this.model.on('change', this.render, this); + }, + + render: function() { + this.$el.html(this.template(this.model.toJSON())); + return this; + } + +}); diff --git a/src/public/signup.html b/src/public/signup.html new file mode 100755 index 0000000..608fd06 --- /dev/null +++ b/src/public/signup.html @@ -0,0 +1,51 @@ + + + + + Signup for getRANTr + + + + + + + + + + + + + + + + + + diff --git a/src/public/styles/ads.scss b/src/public/styles/ads.scss new file mode 100755 index 0000000..0fc9edf --- /dev/null +++ b/src/public/styles/ads.scss @@ -0,0 +1,13 @@ +#ads { + margin: 20px 5px 0 0; + position: fixed; + + .ad { + border: 1px solid #ccc; + background-color: lighten(#ccc, 15%); + border-radius: 3px; + padding: 10px; + margin: 10px 0 20px; + } + +} diff --git a/src/public/styles/base.scss b/src/public/styles/base.scss new file mode 100755 index 0000000..90e50be --- /dev/null +++ b/src/public/styles/base.scss @@ -0,0 +1,107 @@ +/** + * Reset some basic elements + */ +body, h1, h2, h3, h4, h5, h6, +p, blockquote, pre, hr, +dl, dd, ol, ul, figure { + margin: 0; + padding: 0; +} + + + +/** + * Basic styling + */ +body { + font: $base-font-weight #{$base-font-size}/#{$base-line-height} $base-font-family; + color: $text-color; + background-color: $background-color; + -webkit-text-size-adjust: 100%; + -webkit-font-feature-settings: "kern" 1; + -moz-font-feature-settings: "kern" 1; + -o-font-feature-settings: "kern" 1; + font-feature-settings: "kern" 1; + font-kerning: normal; +} + + + +/** + * Set `margin-bottom` to maintain vertical rhythm + */ +h1, h2, h3, h4, h5, h6, +p, blockquote, pre, +ul, ol, dl, figure, +%vertical-rhythm { + margin-bottom: $spacing-unit / 2; +} + + + +/** + * Images + */ +img { + max-width: 100%; + vertical-align: middle; +} + + + +/** + * Figures + */ +figure > img { + display: block; +} + +figcaption { + font-size: $small-font-size; +} + + + +/** + * Lists + */ +ul, ol { + margin-left: $spacing-unit; +} + +li { + > ul, + > ol { + margin-bottom: 0; + } +} + +.pull-right { + float:right; +} +.pull-left { + float: left; +} + + + +/** + * Clearfix + */ +%clearfix { + + &:after { + content: ""; + display: table; + clear: both; + } +} + +.clearfix { + + &:after { + content: ""; + display: table; + clear: both; + } +} diff --git a/src/public/styles/header.scss b/src/public/styles/header.scss new file mode 100755 index 0000000..4146a22 --- /dev/null +++ b/src/public/styles/header.scss @@ -0,0 +1,16 @@ +header { + height: $header-height; + width: 100%; + margin: 0; + position: fixed; + background-color: $color-complement-4; + color: white; + z-index: 2; + + .titlebar { + padding: 10px 20px; + h1 { + margin: 0; padding: 0; + } + } +} diff --git a/src/public/styles/index.scss b/src/public/styles/index.scss new file mode 100755 index 0000000..a882feb --- /dev/null +++ b/src/public/styles/index.scss @@ -0,0 +1,46 @@ + +$text-color: #111; +$text-color-weak: #828282; +$background-color: #fdfdfd; + +$color-primary-0: #FFDEA8; // Main Primary color */ +$color-primary-1: #FFF2DD; +$color-primary-2: #FFE9C4; +$color-primary-3: #ECC480; +$color-primary-4: #CFA156; + +$color-secondary-1-0: #86CB8F; // Main Secondary color (1) */ +$color-secondary-1-1: #CAEACE; +$color-secondary-1-2: #A9DCAF; +$color-secondary-1-3: #63B76D; +$color-secondary-1-4: #43A04E; + +$color-secondary-2-0: #FFACA8; // Main Secondary color (2) */ +$color-secondary-2-1: #FFDEDD; +$color-secondary-2-2: #FFC7C4; +$color-secondary-2-3: #EC8580; +$color-secondary-2-4: #CF5C56; + +$color-complement-0: #7991B0; // Main Complement color */ +$color-complement-1: #C3CFDE; +$color-complement-2: #9FB0C9; +$color-complement-3: #5A769C; +$color-complement-4: #3F5E88; + +$base-font-family: 'Open Sans', sans-serif; +$base-font-size: 16px; +$base-font-weight: 400; +$small-font-size: $base-font-size * 0.875; +$base-line-height: 1.2; +$spacing-unit: 30px; +$header-height: 60px; + + +@import 'base'; +@import 'layout'; +@import 'typography'; +@import 'header'; +@import 'profile'; +@import 'timeline'; +@import 'ads'; +@import 'signup'; diff --git a/src/public/styles/layout.scss b/src/public/styles/layout.scss new file mode 100755 index 0000000..6e33a52 --- /dev/null +++ b/src/public/styles/layout.scss @@ -0,0 +1,19 @@ +.col { + float: left; + margin-top: $header-height; + margin-bottom: $spacing-unit; + padding-right: $spacing-unit; + + &.col-1 { + width: 200px; + } + + &.col-2 { + width: calc(60% - 230px + (#{$spacing-unit} * 2)); + } + + &.col-3 { + width: calc(40% - 230px + (#{$spacing-unit} * 2)); + } + +} diff --git a/src/public/styles/profile.scss b/src/public/styles/profile.scss new file mode 100755 index 0000000..c82e9a0 --- /dev/null +++ b/src/public/styles/profile.scss @@ -0,0 +1,23 @@ +.profile { + padding: 20px; + position: fixed; + top: $header-height; + bottom: 0; + background-color: lighten($color-complement-2, 20%); + + img { + width: 160px; + border-radius: 15px; + padding-bottom: 10px; + } + + .name { + font-size: 18px; + } + + .join { + font-size: 16px; + color: $text-color-weak; + } + +} diff --git a/src/public/styles/signup.scss b/src/public/styles/signup.scss new file mode 100755 index 0000000..8197db3 --- /dev/null +++ b/src/public/styles/signup.scss @@ -0,0 +1,65 @@ +.page-signup { + + width: 100%; + margin: 20px 0; + + .signup-form { + margin: 0 auto; + width: 420px; + background-color: white; + box-shadow: 0px 0px 10px 4px rgba(0,0,0,0.3); + + a:hover, + a:focus, + a:active { + color: black; + } + + h2 { + text-align: center; + padding: 15px; + margin: 0; + background-color: $color-complement-4; + color: white; + } + + .signup-form-preamble { + padding: 15px 0; + text-align: center; + border-bottom: 1px solid #f5f5f5; + } + + .signup-form-wrapper { + padding: 0px 15px; + color: black; + } + + .fields-wrap { + margin: 20px 0; + } + .field-wrap { + margin: 10px auto 20px; + width: 320px; + } + .field-desc { + color: #ddd; + } + + .signup-action { + padding: 20px 0; + width: 100%; + text-align: center; + } + + } + + button { + font-size: 24px; + padding: 4px 8px; + color: white; + background-color: $color-secondary-1-3; + border: 1px solid $color-secondary-1-4; + cursor: pointer; + } + +} diff --git a/src/public/styles/timeline.scss b/src/public/styles/timeline.scss new file mode 100755 index 0000000..8f3f0e4 --- /dev/null +++ b/src/public/styles/timeline.scss @@ -0,0 +1,63 @@ +#add-rant { + + padding: 20px 0; + + label, textarea { + display: block; + margin: 5px 0; + } + + textarea { + width: calc(100% - 14px); + font-size: 16px; + line-height: 20px; + padding: 5px; + height: 70px; + border: 2px solid $color-secondary-1-0; + resize: none; + } + + button { + font-size: 16px; + padding: 4px 8px; + color: white; + background-color: $color-secondary-1-3; + border: 1px solid $color-secondary-1-4; + cursor: pointer; + } + +} + +#timeline { + + margin-top: 40px; + + .rant { + margin: 10px 0 20px; + padding: 10px; + border-bottom: 1px solid $color-complement-2; + + img { + width: 60px; + border-radius: 5px; + position: relative; + left: 0; + } + + .rant-content { + position: relative; + top: -60px; + left: 70px; + width: calc(100% - 70px); + margin-bottom: -60px; + min-height: 60px; + } + form { display: inline; } + .rant-meta { + margin-left: 70px; + } + + + } + +} diff --git a/src/public/styles/typography.scss b/src/public/styles/typography.scss new file mode 100755 index 0000000..954e498 --- /dev/null +++ b/src/public/styles/typography.scss @@ -0,0 +1,4 @@ + +.text-sm { + font-size: 12px; +} diff --git a/src/public/vendor/analytics.js b/src/public/vendor/analytics.js new file mode 100755 index 0000000..bb361c5 --- /dev/null +++ b/src/public/vendor/analytics.js @@ -0,0 +1,36 @@ +/* global window, XMLHttpRequest */ + +(function() { + 'use strict'; + + function trackConversion(callback) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', 'http://www.getrantr.com:9000/api/analytics/conversion'); + xhr.addEventListener('readystatechange', function() { + if (xhr.readyState === 4) { + if (callback) callback(); + } + }); + xhr.send(); + } + + // New Feature! Now we can automatically Intercept all form submissions as a conversion event. + window.addEventListener('submit', function(evt) { + var form = evt.target; + if (form.hasAttribute('ignore')) { return; } + + evt.preventDefault(); + + trackConversion(function() { + form.submit(); + }); + + }, true); + + window.analytics = { + + trackConversion: trackConversion + + }; + +})(); diff --git a/src/public/vendor/backbone.js b/src/public/vendor/backbone.js new file mode 100755 index 0000000..83f88a2 --- /dev/null +++ b/src/public/vendor/backbone.js @@ -0,0 +1,2 @@ +(function(t){var e=typeof self=="object"&&self.self===self&&self||typeof global=="object"&&global.global===global&&global;if(typeof define==="function"&&define.amd){define(["underscore","jquery","exports"],function(i,r,n){e.Backbone=t(e,n,i,r)})}else if(typeof exports!=="undefined"){var i=require("underscore"),r;try{r=require("jquery")}catch(n){}t(e,exports,i,r)}else{e.Backbone=t(e,{},e._,e.jQuery||e.Zepto||e.ender||e.$)}})(function(t,e,i,r){var n=t.Backbone;var s=Array.prototype.slice;e.VERSION="1.3.3";e.$=r;e.noConflict=function(){t.Backbone=n;return this};e.emulateHTTP=false;e.emulateJSON=false;var a=function(t,e,r){switch(t){case 1:return function(){return i[e](this[r])};case 2:return function(t){return i[e](this[r],t)};case 3:return function(t,n){return i[e](this[r],o(t,this),n)};case 4:return function(t,n,s){return i[e](this[r],o(t,this),n,s)};default:return function(){var t=s.call(arguments);t.unshift(this[r]);return i[e].apply(i,t)}}};var h=function(t,e,r){i.each(e,function(e,n){if(i[n])t.prototype[n]=a(e,n,r)})};var o=function(t,e){if(i.isFunction(t))return t;if(i.isObject(t)&&!e._isModel(t))return l(t);if(i.isString(t))return function(e){return e.get(t)};return t};var l=function(t){var e=i.matches(t);return function(t){return e(t.attributes)}};var u=e.Events={};var c=/\s+/;var f=function(t,e,r,n,s){var a=0,h;if(r&&typeof r==="object"){if(n!==void 0&&"context"in s&&s.context===void 0)s.context=n;for(h=i.keys(r);athis.length)n=this.length;if(n<0)n+=this.length+1;var s=[];var a=[];var h=[];var o=[];var l={};var u=e.add;var c=e.merge;var f=e.remove;var d=false;var v=this.comparator&&n==null&&e.sort!==false;var g=i.isString(this.comparator)?this.comparator:null;var p,m;for(m=0;m7);this._useHashChange=this._wantsHashChange&&this._hasHashChange;this._wantsPushState=!!this.options.pushState;this._hasPushState=!!(this.history&&this.history.pushState);this._usePushState=this._wantsPushState&&this._hasPushState;this.fragment=this.getFragment();this.root=("/"+this.root+"/").replace(O,"/");if(this._wantsHashChange&&this._wantsPushState){if(!this._hasPushState&&!this.atRoot()){var e=this.root.slice(0,-1)||"/";this.location.replace(e+"#"+this.getPath());return true}else if(this._hasPushState&&this.atRoot()){this.navigate(this.getHash(),{replace:true})}}if(!this._hasHashChange&&this._wantsHashChange&&!this._usePushState){this.iframe=document.createElement("iframe");this.iframe.src="javascript:0";this.iframe.style.display="none";this.iframe.tabIndex=-1;var r=document.body;var n=r.insertBefore(this.iframe,r.firstChild).contentWindow;n.document.open();n.document.close();n.location.hash="#"+this.fragment}var s=window.addEventListener||function(t,e){return attachEvent("on"+t,e)};if(this._usePushState){s("popstate",this.checkUrl,false)}else if(this._useHashChange&&!this.iframe){s("hashchange",this.checkUrl,false)}else if(this._wantsHashChange){this._checkUrlInterval=setInterval(this.checkUrl,this.interval)}if(!this.options.silent)return this.loadUrl()},stop:function(){var t=window.removeEventListener||function(t,e){return detachEvent("on"+t,e)};if(this._usePushState){t("popstate",this.checkUrl,false)}else if(this._useHashChange&&!this.iframe){t("hashchange",this.checkUrl,false)}if(this.iframe){document.body.removeChild(this.iframe);this.iframe=null}if(this._checkUrlInterval)clearInterval(this._checkUrlInterval);N.started=false},route:function(t,e){this.handlers.unshift({route:t,callback:e})},checkUrl:function(t){var e=this.getFragment();if(e===this.fragment&&this.iframe){e=this.getHash(this.iframe.contentWindow)}if(e===this.fragment)return false;if(this.iframe)this.navigate(e);this.loadUrl()},loadUrl:function(t){if(!this.matchRoot())return false;t=this.fragment=this.getFragment(t);return i.some(this.handlers,function(e){if(e.route.test(t)){e.callback(t);return true}})},navigate:function(t,e){if(!N.started)return false;if(!e||e===true)e={trigger:!!e};t=this.getFragment(t||"");var i=this.root;if(t===""||t.charAt(0)==="?"){i=i.slice(0,-1)||"/"}var r=i+t;t=this.decodeFragment(t.replace(U,""));if(this.fragment===t)return;this.fragment=t;if(this._usePushState){this.history[e.replace?"replaceState":"pushState"]({},document.title,r)}else if(this._wantsHashChange){this._updateHash(this.location,t,e.replace);if(this.iframe&&t!==this.getHash(this.iframe.contentWindow)){var n=this.iframe.contentWindow;if(!e.replace){n.document.open();n.document.close()}this._updateHash(n.location,t,e.replace)}}else{return this.location.assign(r)}if(e.trigger)return this.loadUrl(t)},_updateHash:function(t,e,i){if(i){var r=t.href.replace(/(javascript:|#).*$/,"");t.replace(r+"#"+e)}else{t.hash="#"+e}}});e.history=new N;var q=function(t,e){var r=this;var n;if(t&&i.has(t,"constructor")){n=t.constructor}else{n=function(){return r.apply(this,arguments)}}i.extend(n,r,e);n.prototype=i.create(r.prototype,t);n.prototype.constructor=n;n.__super__=r.prototype;return n};y.extend=x.extend=$.extend=k.extend=N.extend=q;var F=function(){throw new Error('A "url" property or function must be specified')};var B=function(t,e){var i=e.error;e.error=function(r){if(i)i.call(e.context,t,r,e);t.trigger("error",t,r,e)}};return e}); +//# sourceMappingURL=backbone-min.map diff --git a/src/public/vendor/jquery.js b/src/public/vendor/jquery.js new file mode 100755 index 0000000..4c5be4c --- /dev/null +++ b/src/public/vendor/jquery.js @@ -0,0 +1,4 @@ +/*! jQuery v3.1.1 | (c) jQuery Foundation | jquery.org/license */ +!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.1.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):C.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/[^\x20\t\r\n\f]+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R), +a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,ka=/^$|\/(?:java|ecma)script/i,la={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};la.optgroup=la.option,la.tbody=la.tfoot=la.colgroup=la.caption=la.thead,la.th=la.td;function ma(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function na(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=ma(l.appendChild(f),"script"),j&&na(g),c){k=0;while(f=g[k++])ka.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var qa=d.documentElement,ra=/^key/,sa=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ta=/^([^.]*)(?:\.(.+)|)/;function ua(){return!0}function va(){return!1}function wa(){try{return d.activeElement}catch(a){}}function xa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)xa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=va;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(qa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i\x20\t\r\n\f]*)[^>]*)\/>/gi,za=/\s*$/g;function Da(a,b){return r.nodeName(a,"table")&&r.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a:a}function Ea(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Fa(a){var b=Ba.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ga(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(V.hasData(a)&&(f=V.access(a),g=V.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c1&&"string"==typeof q&&!o.checkClone&&Aa.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ia(f,b,c,d)});if(m&&(e=pa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(ma(e,"script"),Ea),i=h.length;l")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=ma(h),f=ma(a),d=0,e=f.length;d0&&na(g,!i&&ma(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(T(c)){if(b=c[V.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[V.expando]=void 0}c[W.expando]&&(c[W.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ja(this,a,!0)},remove:function(a){return Ja(this,a)},text:function(a){return S(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.appendChild(a)}})},prepend:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(ma(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return S(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!za.test(a)&&!la[(ja.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c1)}});function Ya(a,b,c,d,e){return new Ya.prototype.init(a,b,c,d,e)}r.Tween=Ya,Ya.prototype={constructor:Ya,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=Ya.propHooks[this.prop];return a&&a.get?a.get(this):Ya.propHooks._default.get(this)},run:function(a){var b,c=Ya.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Ya.propHooks._default.set(this),this}},Ya.prototype.init.prototype=Ya.prototype,Ya.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},Ya.propHooks.scrollTop=Ya.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=Ya.prototype.init,r.fx.step={};var Za,$a,_a=/^(?:toggle|show|hide)$/,ab=/queueHooks$/;function bb(){$a&&(a.requestAnimationFrame(bb),r.fx.tick())}function cb(){return a.setTimeout(function(){Za=void 0}),Za=r.now()}function db(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ba[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function eb(a,b,c){for(var d,e=(hb.tweeners[b]||[]).concat(hb.tweeners["*"]),f=0,g=e.length;f1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?ib:void 0)), +void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&r.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(K);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),ib={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=jb[b]||r.find.attr;jb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=jb[g],jb[g]=e,e=null!=c(a,b,d)?g:null,jb[g]=f),e}});var kb=/^(?:input|select|textarea|button)$/i,lb=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return S(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):kb.test(a.nodeName)||lb.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function mb(a){var b=a.match(K)||[];return b.join(" ")}function nb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,nb(this)))});if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,nb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,nb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(K)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=nb(this),b&&V.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":V.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+mb(nb(c))+" ").indexOf(b)>-1)return!0;return!1}});var ob=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":r.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(ob,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:mb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(r.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var pb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!pb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,pb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(V.get(h,"events")||{})[b.type]&&V.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&T(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!T(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=V.access(d,b);e||d.addEventListener(a,c,!0),V.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=V.access(d,b)-1;e?V.access(d,b,e):(d.removeEventListener(a,c,!0),V.remove(d,b))}}});var qb=a.location,rb=r.now(),sb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var tb=/\[\]$/,ub=/\r?\n/g,vb=/^(?:submit|button|image|reset|file)$/i,wb=/^(?:input|select|textarea|keygen)/i;function xb(a,b,c,d){var e;if(r.isArray(b))r.each(b,function(b,e){c||tb.test(a)?d(a,e):xb(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)xb(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(r.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)xb(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&wb.test(this.nodeName)&&!vb.test(a)&&(this.checked||!ia.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:r.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(ub,"\r\n")}}):{name:b.name,value:c.replace(ub,"\r\n")}}).get()}});var yb=/%20/g,zb=/#.*$/,Ab=/([?&])_=[^&]*/,Bb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Cb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Db=/^(?:GET|HEAD)$/,Eb=/^\/\//,Fb={},Gb={},Hb="*/".concat("*"),Ib=d.createElement("a");Ib.href=qb.href;function Jb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(K)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Kb(a,b,c,d){var e={},f=a===Gb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Lb(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Mb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Nb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:qb.href,type:"GET",isLocal:Cb.test(qb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Hb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Lb(Lb(a,r.ajaxSettings),b):Lb(r.ajaxSettings,a)},ajaxPrefilter:Jb(Fb),ajaxTransport:Jb(Gb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Bb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||qb.href)+"").replace(Eb,qb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(K)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Ib.protocol+"//"+Ib.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Kb(Fb,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Db.test(o.type),f=o.url.replace(zb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(yb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(sb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Ab,"$1"),n=(sb.test(f)?"&":"?")+"_="+rb++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Hb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Kb(Gb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Mb(o,y,d)),v=Nb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Ob={0:200,1223:204},Pb=r.ajaxSettings.xhr();o.cors=!!Pb&&"withCredentials"in Pb,o.ajax=Pb=!!Pb,r.ajaxTransport(function(b){var c,d;if(o.cors||Pb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Ob[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r("