From 09a59da1e0166077575aa97d9e7d7b5086f4b3a9 Mon Sep 17 00:00:00 2001 From: nau Date: Thu, 25 Aug 2016 19:57:22 +0200 Subject: [PATCH] app initializated --- README.md | 2 +- app/.bowerrc | 3 + app/.editorconfig | 14 + app/.gitignore | 6 + app/bower.json | 7 + app/config.xml | 22 + app/gulpfile.js | 51 + app/hooks/README.md | 83 + .../after_prepare/010_add_platform_class.js | 94 + app/ionic.project | 4 + app/package.json | 26 + app/scss/ionic.app.scss | 23 + app/www/css/style.css | 1 + app/www/img/carimg/aerial-tramway.png | Bin 0 -> 4247 bytes app/www/img/carimg/aeroplane.png | Bin 0 -> 13091 bytes app/www/img/carimg/airplane.png | Bin 0 -> 6880 bytes app/www/img/carimg/auto-ricksaw.png | Bin 0 -> 11888 bytes app/www/img/carimg/beetle.png | Bin 0 -> 13278 bytes app/www/img/carimg/bike.png | Bin 0 -> 7945 bytes app/www/img/carimg/boat-1.png | Bin 0 -> 14793 bytes app/www/img/carimg/boat-2.png | Bin 0 -> 13772 bytes app/www/img/carimg/boat.png | Bin 0 -> 12519 bytes app/www/img/carimg/bus.png | Bin 0 -> 5759 bytes app/www/img/carimg/car.png | Bin 0 -> 8479 bytes app/www/img/carimg/catamaran.png | Bin 0 -> 11066 bytes app/www/img/carimg/chairlift.png | Bin 0 -> 8409 bytes app/www/img/carimg/classic-car.png | Bin 0 -> 9734 bytes app/www/img/carimg/double-decker-bus.png | Bin 0 -> 6219 bytes app/www/img/carimg/helicopter.png | Bin 0 -> 10790 bytes app/www/img/carimg/hot-air-balloon.png | Bin 0 -> 12310 bytes app/www/img/carimg/house-boat.png | Bin 0 -> 9701 bytes app/www/img/carimg/jeep.png | Bin 0 -> 11329 bytes app/www/img/carimg/jet-boating.png | Bin 0 -> 13481 bytes app/www/img/carimg/locomotive.png | Bin 0 -> 12344 bytes app/www/img/carimg/lorry.png | Bin 0 -> 5248 bytes app/www/img/carimg/minibus.png | Bin 0 -> 5625 bytes app/www/img/carimg/minivan-taxi.png | Bin 0 -> 9517 bytes app/www/img/carimg/minivan.png | Bin 0 -> 9930 bytes app/www/img/carimg/monorail.png | Bin 0 -> 8348 bytes app/www/img/carimg/motorbike.png | Bin 0 -> 10760 bytes app/www/img/carimg/quad.png | Bin 0 -> 13266 bytes app/www/img/carimg/sailboat.png | Bin 0 -> 9980 bytes app/www/img/carimg/sailing-ship.png | Bin 0 -> 9919 bytes app/www/img/carimg/school-bus.png | Bin 0 -> 8916 bytes app/www/img/carimg/sidecar.png | Bin 0 -> 12857 bytes app/www/img/carimg/spaceship.png | Bin 0 -> 6114 bytes app/www/img/carimg/sport-car.png | Bin 0 -> 8863 bytes app/www/img/carimg/station-wagon.png | Bin 0 -> 10002 bytes app/www/img/carimg/submarine.png | Bin 0 -> 15338 bytes app/www/img/carimg/taxi.png | Bin 0 -> 9072 bytes app/www/img/carimg/tractor.png | Bin 0 -> 9819 bytes app/www/img/carimg/train-1.png | Bin 0 -> 8723 bytes app/www/img/carimg/train-2.png | Bin 0 -> 12986 bytes app/www/img/carimg/train.png | Bin 0 -> 5561 bytes app/www/img/carimg/tram.png | Bin 0 -> 7219 bytes app/www/img/carimg/tricycle.png | Bin 0 -> 9723 bytes app/www/img/carimg/trolleybus.png | Bin 0 -> 6105 bytes app/www/img/carimg/truck.png | Bin 0 -> 3747 bytes app/www/img/carimg/ufo.png | Bin 0 -> 5433 bytes app/www/img/carimg/underground.png | Bin 0 -> 10526 bytes app/www/img/carimg/van.png | Bin 0 -> 8144 bytes app/www/img/carimg/volskwagen-van.png | Bin 0 -> 12794 bytes app/www/img/ionic.png | Bin 0 -> 4757 bytes app/www/index.html | 29 + app/www/js/app.js | 73 + app/www/js/controllers.js | 59 + app/www/lib/ionic/css/ionic.css | 9813 +++ app/www/lib/ionic/css/ionic.min.css | 23 + app/www/lib/ionic/fonts/ionicons.eot | Bin 0 -> 120724 bytes app/www/lib/ionic/fonts/ionicons.svg | 2230 + app/www/lib/ionic/fonts/ionicons.ttf | Bin 0 -> 188508 bytes app/www/lib/ionic/fonts/ionicons.woff | Bin 0 -> 67904 bytes .../ionic/js/angular-ui/angular-ui-router.js | 4232 + .../js/angular-ui/angular-ui-router.min.js | 7 + .../lib/ionic/js/angular/angular-animate.js | 4115 + .../ionic/js/angular/angular-animate.min.js | 56 + .../lib/ionic/js/angular/angular-resource.js | 768 + .../ionic/js/angular/angular-resource.min.js | 15 + .../lib/ionic/js/angular/angular-sanitize.js | 717 + .../ionic/js/angular/angular-sanitize.min.js | 15 + app/www/lib/ionic/js/angular/angular.js | 30714 +++++++ app/www/lib/ionic/js/angular/angular.min.js | 311 + app/www/lib/ionic/js/ionic-angular.js | 14391 ++++ app/www/lib/ionic/js/ionic-angular.min.js | 18 + app/www/lib/ionic/js/ionic.bundle.js | 67569 ++++++++++++++++ app/www/lib/ionic/js/ionic.bundle.min.js | 472 + app/www/lib/ionic/js/ionic.js | 13356 +++ app/www/lib/ionic/js/ionic.min.js | 20 + app/www/lib/ionic/scss/_action-sheet.scss | 170 + app/www/lib/ionic/scss/_animations.scss | 48 + app/www/lib/ionic/scss/_backdrop.scss | 24 + app/www/lib/ionic/scss/_badge.scss | 62 + app/www/lib/ionic/scss/_bar.scss | 417 + app/www/lib/ionic/scss/_button-bar.scss | 92 + app/www/lib/ionic/scss/_button.scss | 252 + app/www/lib/ionic/scss/_checkbox.scss | 180 + app/www/lib/ionic/scss/_form.scss | 327 + app/www/lib/ionic/scss/_grid.scss | 159 + app/www/lib/ionic/scss/_items.scss | 825 + app/www/lib/ionic/scss/_list.scss | 125 + app/www/lib/ionic/scss/_loading.scss | 51 + app/www/lib/ionic/scss/_menu.scss | 70 + app/www/lib/ionic/scss/_mixins.scss | 642 + app/www/lib/ionic/scss/_modal.scss | 102 + app/www/lib/ionic/scss/_platform.scss | 77 + app/www/lib/ionic/scss/_popover.scss | 168 + app/www/lib/ionic/scss/_popup.scss | 110 + app/www/lib/ionic/scss/_progress.scss | 11 + app/www/lib/ionic/scss/_radio.scss | 47 + app/www/lib/ionic/scss/_range.scss | 160 + app/www/lib/ionic/scss/_refresher.scss | 113 + app/www/lib/ionic/scss/_reset.scss | 365 + app/www/lib/ionic/scss/_scaffolding.scss | 291 + app/www/lib/ionic/scss/_select.scss | 142 + app/www/lib/ionic/scss/_slide-box.scss | 71 + app/www/lib/ionic/scss/_slides.scss | 529 + app/www/lib/ionic/scss/_spinner.scss | 100 + app/www/lib/ionic/scss/_tabs.scss | 542 + app/www/lib/ionic/scss/_toggle.scss | 198 + app/www/lib/ionic/scss/_transitions.scss | 188 + app/www/lib/ionic/scss/_type.scss | 166 + app/www/lib/ionic/scss/_util.scss | 296 + app/www/lib/ionic/scss/_variables.scss | 764 + app/www/lib/ionic/scss/ionic.scss | 54 + .../ionic/scss/ionicons/_ionicons-font.scss | 28 + .../ionic/scss/ionicons/_ionicons-icons.scss | 1473 + .../scss/ionicons/_ionicons-variables.scss | 741 + app/www/lib/ionic/scss/ionicons/ionicons.scss | 16 + app/www/lib/ionic/version.json | 6 + app/www/templates/browse.html | 5 + app/www/templates/login.html | 25 + app/www/templates/menu.html | 39 + app/www/templates/playlists.html | 16 + app/www/templates/search.html | 5 + app/www/templates/travel.html | 5 + app/www/templates/travels.html | 12 + 136 files changed, 159647 insertions(+), 1 deletion(-) create mode 100644 app/.bowerrc create mode 100644 app/.editorconfig create mode 100644 app/.gitignore create mode 100644 app/bower.json create mode 100644 app/config.xml create mode 100644 app/gulpfile.js create mode 100644 app/hooks/README.md create mode 100755 app/hooks/after_prepare/010_add_platform_class.js create mode 100644 app/ionic.project create mode 100644 app/package.json create mode 100644 app/scss/ionic.app.scss create mode 100644 app/www/css/style.css create mode 100644 app/www/img/carimg/aerial-tramway.png create mode 100644 app/www/img/carimg/aeroplane.png create mode 100644 app/www/img/carimg/airplane.png create mode 100644 app/www/img/carimg/auto-ricksaw.png create mode 100644 app/www/img/carimg/beetle.png create mode 100644 app/www/img/carimg/bike.png create mode 100644 app/www/img/carimg/boat-1.png create mode 100644 app/www/img/carimg/boat-2.png create mode 100644 app/www/img/carimg/boat.png create mode 100644 app/www/img/carimg/bus.png create mode 100644 app/www/img/carimg/car.png create mode 100644 app/www/img/carimg/catamaran.png create mode 100644 app/www/img/carimg/chairlift.png create mode 100644 app/www/img/carimg/classic-car.png create mode 100644 app/www/img/carimg/double-decker-bus.png create mode 100644 app/www/img/carimg/helicopter.png create mode 100644 app/www/img/carimg/hot-air-balloon.png create mode 100644 app/www/img/carimg/house-boat.png create mode 100644 app/www/img/carimg/jeep.png create mode 100644 app/www/img/carimg/jet-boating.png create mode 100644 app/www/img/carimg/locomotive.png create mode 100644 app/www/img/carimg/lorry.png create mode 100644 app/www/img/carimg/minibus.png create mode 100644 app/www/img/carimg/minivan-taxi.png create mode 100644 app/www/img/carimg/minivan.png create mode 100644 app/www/img/carimg/monorail.png create mode 100644 app/www/img/carimg/motorbike.png create mode 100644 app/www/img/carimg/quad.png create mode 100644 app/www/img/carimg/sailboat.png create mode 100644 app/www/img/carimg/sailing-ship.png create mode 100644 app/www/img/carimg/school-bus.png create mode 100644 app/www/img/carimg/sidecar.png create mode 100644 app/www/img/carimg/spaceship.png create mode 100644 app/www/img/carimg/sport-car.png create mode 100644 app/www/img/carimg/station-wagon.png create mode 100644 app/www/img/carimg/submarine.png create mode 100644 app/www/img/carimg/taxi.png create mode 100644 app/www/img/carimg/tractor.png create mode 100644 app/www/img/carimg/train-1.png create mode 100644 app/www/img/carimg/train-2.png create mode 100644 app/www/img/carimg/train.png create mode 100644 app/www/img/carimg/tram.png create mode 100644 app/www/img/carimg/tricycle.png create mode 100644 app/www/img/carimg/trolleybus.png create mode 100644 app/www/img/carimg/truck.png create mode 100644 app/www/img/carimg/ufo.png create mode 100644 app/www/img/carimg/underground.png create mode 100644 app/www/img/carimg/van.png create mode 100644 app/www/img/carimg/volskwagen-van.png create mode 100644 app/www/img/ionic.png create mode 100644 app/www/index.html create mode 100644 app/www/js/app.js create mode 100644 app/www/js/controllers.js create mode 100644 app/www/lib/ionic/css/ionic.css create mode 100644 app/www/lib/ionic/css/ionic.min.css create mode 100644 app/www/lib/ionic/fonts/ionicons.eot create mode 100644 app/www/lib/ionic/fonts/ionicons.svg create mode 100644 app/www/lib/ionic/fonts/ionicons.ttf create mode 100644 app/www/lib/ionic/fonts/ionicons.woff create mode 100644 app/www/lib/ionic/js/angular-ui/angular-ui-router.js create mode 100644 app/www/lib/ionic/js/angular-ui/angular-ui-router.min.js create mode 100644 app/www/lib/ionic/js/angular/angular-animate.js create mode 100644 app/www/lib/ionic/js/angular/angular-animate.min.js create mode 100644 app/www/lib/ionic/js/angular/angular-resource.js create mode 100644 app/www/lib/ionic/js/angular/angular-resource.min.js create mode 100644 app/www/lib/ionic/js/angular/angular-sanitize.js create mode 100644 app/www/lib/ionic/js/angular/angular-sanitize.min.js create mode 100644 app/www/lib/ionic/js/angular/angular.js create mode 100644 app/www/lib/ionic/js/angular/angular.min.js create mode 100644 app/www/lib/ionic/js/ionic-angular.js create mode 100644 app/www/lib/ionic/js/ionic-angular.min.js create mode 100644 app/www/lib/ionic/js/ionic.bundle.js create mode 100644 app/www/lib/ionic/js/ionic.bundle.min.js create mode 100644 app/www/lib/ionic/js/ionic.js create mode 100644 app/www/lib/ionic/js/ionic.min.js create mode 100644 app/www/lib/ionic/scss/_action-sheet.scss create mode 100644 app/www/lib/ionic/scss/_animations.scss create mode 100644 app/www/lib/ionic/scss/_backdrop.scss create mode 100644 app/www/lib/ionic/scss/_badge.scss create mode 100644 app/www/lib/ionic/scss/_bar.scss create mode 100644 app/www/lib/ionic/scss/_button-bar.scss create mode 100644 app/www/lib/ionic/scss/_button.scss create mode 100644 app/www/lib/ionic/scss/_checkbox.scss create mode 100644 app/www/lib/ionic/scss/_form.scss create mode 100644 app/www/lib/ionic/scss/_grid.scss create mode 100644 app/www/lib/ionic/scss/_items.scss create mode 100644 app/www/lib/ionic/scss/_list.scss create mode 100644 app/www/lib/ionic/scss/_loading.scss create mode 100644 app/www/lib/ionic/scss/_menu.scss create mode 100644 app/www/lib/ionic/scss/_mixins.scss create mode 100644 app/www/lib/ionic/scss/_modal.scss create mode 100644 app/www/lib/ionic/scss/_platform.scss create mode 100644 app/www/lib/ionic/scss/_popover.scss create mode 100644 app/www/lib/ionic/scss/_popup.scss create mode 100644 app/www/lib/ionic/scss/_progress.scss create mode 100644 app/www/lib/ionic/scss/_radio.scss create mode 100644 app/www/lib/ionic/scss/_range.scss create mode 100644 app/www/lib/ionic/scss/_refresher.scss create mode 100644 app/www/lib/ionic/scss/_reset.scss create mode 100644 app/www/lib/ionic/scss/_scaffolding.scss create mode 100644 app/www/lib/ionic/scss/_select.scss create mode 100644 app/www/lib/ionic/scss/_slide-box.scss create mode 100644 app/www/lib/ionic/scss/_slides.scss create mode 100644 app/www/lib/ionic/scss/_spinner.scss create mode 100644 app/www/lib/ionic/scss/_tabs.scss create mode 100644 app/www/lib/ionic/scss/_toggle.scss create mode 100644 app/www/lib/ionic/scss/_transitions.scss create mode 100644 app/www/lib/ionic/scss/_type.scss create mode 100644 app/www/lib/ionic/scss/_util.scss create mode 100644 app/www/lib/ionic/scss/_variables.scss create mode 100644 app/www/lib/ionic/scss/ionic.scss create mode 100644 app/www/lib/ionic/scss/ionicons/_ionicons-font.scss create mode 100644 app/www/lib/ionic/scss/ionicons/_ionicons-icons.scss create mode 100644 app/www/lib/ionic/scss/ionicons/_ionicons-variables.scss create mode 100644 app/www/lib/ionic/scss/ionicons/ionicons.scss create mode 100644 app/www/lib/ionic/version.json create mode 100644 app/www/templates/browse.html create mode 100644 app/www/templates/login.html create mode 100644 app/www/templates/menu.html create mode 100644 app/www/templates/playlists.html create mode 100644 app/www/templates/search.html create mode 100644 app/www/templates/travel.html create mode 100644 app/www/templates/travels.html diff --git a/README.md b/README.md index 916091d..9f43559 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Nodejs + Express + MongoDB **Frontend:** - Angularjs + Materializecss + Angularjs + Ionic + MaterialDesign ##Project Structure: diff --git a/app/.bowerrc b/app/.bowerrc new file mode 100644 index 0000000..e28246d --- /dev/null +++ b/app/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory": "www/lib" +} diff --git a/app/.editorconfig b/app/.editorconfig new file mode 100644 index 0000000..aca2523 --- /dev/null +++ b/app/.editorconfig @@ -0,0 +1,14 @@ +# http://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..8f7ddd3 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,6 @@ +# Specifies intentionally untracked files to ignore when using Git +# http://git-scm.com/docs/gitignore + +node_modules/ +platforms/ +plugins/ diff --git a/app/bower.json b/app/bower.json new file mode 100644 index 0000000..2721960 --- /dev/null +++ b/app/bower.json @@ -0,0 +1,7 @@ +{ + "name": "HelloIonic", + "private": "true", + "devDependencies": { + "ionic": "driftyco/ionic-bower#1.3.1" + } +} diff --git a/app/config.xml b/app/config.xml new file mode 100644 index 0000000..3c2b1b4 --- /dev/null +++ b/app/config.xml @@ -0,0 +1,22 @@ + + + app + + An Ionic Framework and Cordova project. + + + Your Name Here + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/gulpfile.js b/app/gulpfile.js new file mode 100644 index 0000000..5c283be --- /dev/null +++ b/app/gulpfile.js @@ -0,0 +1,51 @@ +var gulp = require('gulp'); +var gutil = require('gulp-util'); +var bower = require('bower'); +var concat = require('gulp-concat'); +var sass = require('gulp-sass'); +var minifyCss = require('gulp-minify-css'); +var rename = require('gulp-rename'); +var sh = require('shelljs'); + +var paths = { + sass: ['./scss/**/*.scss'] +}; + +gulp.task('default', ['sass']); + +gulp.task('sass', function(done) { + gulp.src('./scss/ionic.app.scss') + .pipe(sass()) + .on('error', sass.logError) + .pipe(gulp.dest('./www/css/')) + .pipe(minifyCss({ + keepSpecialComments: 0 + })) + .pipe(rename({ extname: '.min.css' })) + .pipe(gulp.dest('./www/css/')) + .on('end', done); +}); + +gulp.task('watch', function() { + gulp.watch(paths.sass, ['sass']); +}); + +gulp.task('install', ['git-check'], function() { + return bower.commands.install() + .on('log', function(data) { + gutil.log('bower', gutil.colors.cyan(data.id), data.message); + }); +}); + +gulp.task('git-check', function(done) { + if (!sh.which('git')) { + console.log( + ' ' + gutil.colors.red('Git is not installed.'), + '\n Git, the version control system, is required to download Ionic.', + '\n Download git here:', gutil.colors.cyan('http://git-scm.com/downloads') + '.', + '\n Once git is installed, run \'' + gutil.colors.cyan('gulp install') + '\' again.' + ); + process.exit(1); + } + done(); +}); diff --git a/app/hooks/README.md b/app/hooks/README.md new file mode 100644 index 0000000..d2563ea --- /dev/null +++ b/app/hooks/README.md @@ -0,0 +1,83 @@ + +# Cordova Hooks + +This directory may contain scripts used to customize cordova commands. This +directory used to exist at `.cordova/hooks`, but has now been moved to the +project root. Any scripts you add to these directories will be executed before +and after the commands corresponding to the directory name. Useful for +integrating your own build systems or integrating with version control systems. + +__Remember__: Make your scripts executable. + +## Hook Directories +The following subdirectories will be used for hooks: + + after_build/ + after_compile/ + after_docs/ + after_emulate/ + after_platform_add/ + after_platform_rm/ + after_platform_ls/ + after_plugin_add/ + after_plugin_ls/ + after_plugin_rm/ + after_plugin_search/ + after_prepare/ + after_run/ + after_serve/ + before_build/ + before_compile/ + before_docs/ + before_emulate/ + before_platform_add/ + before_platform_rm/ + before_platform_ls/ + before_plugin_add/ + before_plugin_ls/ + before_plugin_rm/ + before_plugin_search/ + before_prepare/ + before_run/ + before_serve/ + pre_package/ <-- Windows 8 and Windows Phone only. + +## Script Interface + +All scripts are run from the project's root directory and have the root directory passes as the first argument. All other options are passed to the script using environment variables: + +* CORDOVA_VERSION - The version of the Cordova-CLI. +* CORDOVA_PLATFORMS - Comma separated list of platforms that the command applies to (e.g.: android, ios). +* CORDOVA_PLUGINS - Comma separated list of plugin IDs that the command applies to (e.g.: org.apache.cordova.file, org.apache.cordova.file-transfer) +* CORDOVA_HOOK - Path to the hook that is being executed. +* CORDOVA_CMDLINE - The exact command-line arguments passed to cordova (e.g.: cordova run ios --emulate) + +If a script returns a non-zero exit code, then the parent cordova command will be aborted. + + +## Writing hooks + +We highly recommend writting your hooks using Node.js so that they are +cross-platform. Some good examples are shown here: + +[http://devgirl.org/2013/11/12/three-hooks-your-cordovaphonegap-project-needs/](http://devgirl.org/2013/11/12/three-hooks-your-cordovaphonegap-project-needs/) + diff --git a/app/hooks/after_prepare/010_add_platform_class.js b/app/hooks/after_prepare/010_add_platform_class.js new file mode 100755 index 0000000..bda3e41 --- /dev/null +++ b/app/hooks/after_prepare/010_add_platform_class.js @@ -0,0 +1,94 @@ +#!/usr/bin/env node + +// Add Platform Class +// v1.0 +// Automatically adds the platform class to the body tag +// after the `prepare` command. By placing the platform CSS classes +// directly in the HTML built for the platform, it speeds up +// rendering the correct layout/style for the specific platform +// instead of waiting for the JS to figure out the correct classes. + +var fs = require('fs'); +var path = require('path'); + +var rootdir = process.argv[2]; + +function addPlatformBodyTag(indexPath, platform) { + // add the platform class to the body tag + try { + var platformClass = 'platform-' + platform; + var cordovaClass = 'platform-cordova platform-webview'; + + var html = fs.readFileSync(indexPath, 'utf8'); + + var bodyTag = findBodyTag(html); + if(!bodyTag) return; // no opening body tag, something's wrong + + if(bodyTag.indexOf(platformClass) > -1) return; // already added + + var newBodyTag = bodyTag; + + var classAttr = findClassAttr(bodyTag); + if(classAttr) { + // body tag has existing class attribute, add the classname + var endingQuote = classAttr.substring(classAttr.length-1); + var newClassAttr = classAttr.substring(0, classAttr.length-1); + newClassAttr += ' ' + platformClass + ' ' + cordovaClass + endingQuote; + newBodyTag = bodyTag.replace(classAttr, newClassAttr); + + } else { + // add class attribute to the body tag + newBodyTag = bodyTag.replace('>', ' class="' + platformClass + ' ' + cordovaClass + '">'); + } + + html = html.replace(bodyTag, newBodyTag); + + fs.writeFileSync(indexPath, html, 'utf8'); + + process.stdout.write('add to body class: ' + platformClass + '\n'); + } catch(e) { + process.stdout.write(e); + } +} + +function findBodyTag(html) { + // get the body tag + try{ + return html.match(/])(.*?)>/gi)[0]; + }catch(e){} +} + +function findClassAttr(bodyTag) { + // get the body tag's class attribute + try{ + return bodyTag.match(/ class=["|'](.*?)["|']/gi)[0]; + }catch(e){} +} + +if (rootdir) { + + // go through each of the platform directories that have been prepared + var platforms = (process.env.CORDOVA_PLATFORMS ? process.env.CORDOVA_PLATFORMS.split(',') : []); + + for(var x=0; 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 literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/aeroplane.png b/app/www/img/carimg/aeroplane.png new file mode 100644 index 0000000000000000000000000000000000000000..87dbecb6a18faca8c26b248e0ad8273f98a58d92 GIT binary patch literal 13091 zcmb_@cT`hd)9*0a01gT2zNDUB*NQVdlDpiVdJw|$NdH?_z^{-zw z0{|%4gaR;H@M|aZ^8xrp9i**)3kH57VRs&a_jLZ(t%E>%9OZ&&Uqpq156|4ywYqDL zaK9Vk9OwpwgoH?X`N4x+o&DV;5rH14O*L)+kS)=_dgWH=^u}yhhw+!=fq=xgcUdYr zo?2clXU(pb`(>HsKR0W=apOkLQJa_cSef>n>q2F3BtE4Wlz$Y@&uYDEewF(P=}>&b zTy}& zMZxD^XlQ|K0BZ317aB;A4FKv2{2L7j^2)!%=&wNkCNGMX|H4dumjF&ynN&VdHaN*v z-$!3}8UT9x>0_7#eiByB-QxkiGx17juao+$) zkNI@N$HO0dAYYGbo@N2Q3*6WzuuDq=z}e!dZG$}EW6B%QN^ZC10l&DYAb{2@HFs!t z;z@A6U;Zwo19;AW;F!5RrvrliX`$(WxPMx;bif4}&{oR^^{r1pZaRTHugB9}E~d6} zsCF~?XULqSXCS#7dnMIDcLgdBC&Q(orEEMUJp_9-&2d063w?~2#_%_-!1^1Dx6AmOVxtu%UH zE^7WQ!gEifZu!xTGfWR6`8rAJ9Tt9vk!XnEjpL*VBP~Ar4hzmG(+W@1t%P4}QIdl) zUEO{Wj$T_Jj|h7_2l6h8FciZf!^S-e!3;!|9aw{b?0g|uNqc6)eX>}dUw{mR8 zI=q|QY$9f}7hfWF?LXs!lsM^{e0jU{X#6TIgT??<`Lp%`U;iYOW;KQgQV8^xCi|v# zO6eGyZ$>#LaZ5Ue@IEeSoqS~nQ>yzJWQaA$W^lUyVTIGbgP7M|f)OQll|MVV)NVj- z*KZ~IvR-J3o(;J4U@a?YLFrR|JUM6Qupkgp9i&cz%KVJNOa8PRBoVCp_oPUe#y>Y( z$-AjNjRP;4&tKwvqx9@NP3?2wpiBm&U5SzqpP|_pp4|KyrO>AYcFJq9iz#^QgxD&L zH-GZDyaO=WQSupv{?rMA+&XkLUY={dS&;sF&eBl))@D@kV_9n8vaX47B*qTbV{Qj1 z9NF&X)BLIqGr0ePv66^=&ta5U_2k>Jl3gp2B*@+Cb@H1PHJ`W|e~|u-aRPK1YQoYlgjgD3zVGuwN!{`pzX%;Lt^8KXUuPTho=q3n1VFg~gVeQDoLH-&D^B zNX5GLa)8#NvbaJFT84(IqgOZ|?h9QBQV~^mBoW*}L~b`EZ|9yZWk%QeU;p@??k1Le zo&{j?F5rlFUV1Gd{4B^*u(qRBj)Qs z8=A|iowGEEtePPg-FQ2Bwj4GjbR`KCQ^HhDh{iu7e&=fv?MdZNWb|_7j$MRfz}C}1 z!l6~xd7$Ms^@&F}2jit>AvCqc@jI8Z`x%tKkc+-o?(iPYUk63B<}Jty&TJ6&L@aLQ zj>s5e5u%*k7xeY9TCw59tqg4so{y2o$f~xXGy5}VYfxu7-r5i9{;K!n8i#T@3z7J> zduR&Qqk<)7X!!WY=Wr%R)idPHo74IFE-bk^vF>7=ZvyL#hv*l~#-!5QnEfrL({AKw zG^SahXAT|M^Qe)bY*)_JU2BCoB!5_vc{)ajlD87%AR$a_(ryq1LZVGui8buEGg~Y& z?5OpU9J3ip1y(qfN0=A~VT(t*-z?|D$@3Ch<;uI(s76w7^%VL1 zxu}$~QrL~hx*#&ClOO7!%6-}govGJWcif9F*aqVRX9@)+m`4WB2M<*~DpYyG3+lAa#oY?52k%E*k#ho&pX9?tHfm=97{mm6vl z8#u18G9*&Z^1DE?E|AWux_QDV#p{4%cB}MC!a?{#7)jcK=j^izS*^+CnF39sUP0$n%CH^;U+xFj@Z?tl;|BS~C*taY3=OV$h-)(#!ghJ0q>$zpJVrhGi;FJ9EF>L_B=aP*+d zmHFVrXibPo8(0KQks<98rY_}MNyDh7?ym@z0(bCj7RzQj{A7ep+6SKQ4r4QN-y}FV zxvz`XJ*}Rn2|fTjQCZrwjNhf*HU=hJnKw-&q1(qctmROyDmwsm9Xp{!etMiJHuWwp zMyv50e9)V4xf}GZrZ<8=_r4|Co^+a{1624nB@`9?2y^aig zT(>RihXu^LJh~moaF0dFpX~I-DC?Szr|w(5bP&O2;YvXZaMk%hqki@}$EqpEslzkx z>}pNS^gttlSZyqF%|##*KQ~6Nim_)%lykOpPhNYCub!nLrNuuV`@mUJbYS~ObnV5E97Iqm{$t-;p(_rL1 zH2xP>loU~9eHS@Y;LoAl;$H5CvBT8Y{+v5aY7^u4k43`s;cmR!H-L8tmi2RUw?RBx zZoXG%)4I&CLKMY+9>m8K#T#D~iFvc9Ht{U`821H)vnisoB7?)|edIi^aBsnBfZmKc zxR-WWJJdyq#vd8~TvEV8vc~5aUzHo<%9>h`7x%$B)8b&>YI)8h+^cZ*^JmsUc+bul zDbE0^l`iZF2j{&iNM+URGD%XHWHWX2bvw5>D2?v7XH}t2>>Lb3Im7t0clLhL9`dMcgenGTpi1 z;9sW`udx6PNhKXtrFPWymR}^Nhd?KpsbW5V1P^FH_@tgf_vPOv#5BVNPRqY`DSdG~ zb$0s4)mu&@FhKEf$&)|URj_Nf#lP27+V4axL;odUx2- zbWk|TsY@HA1^Ac3_E@L7$awuzaYi|U(O2lIPbxWi*yhu?Ee zqUz15@AmS$1H5@;_v7UKdORT)3y)6Nx2B_m@TR*}kaxe%E{!DZfu@m@6vY(*MH=XS zl&;W;6)&GZ!@K9|wvFNZfg##EV5*gtlIuGo4pMi~p)S5?o~Y=&Vz=lQ`&0Ok-lMg+ zwYrI#8JZ}W3gFj`U17zdbP0|Oo9^?{i|@zGFQIzVb*>*+CrhyB8A*uePQ zxy*?w--A{eY|oM8_GQQ z#n-+5Ys0Qz=ue3?RJ7j^K@lUAAXGvHpW8HDc?x>A_sXomH!uc7je?zWWcn=>$tSn2 z*eTZ13@GgSospM&ouYYp+hz#<*cAAKWJ8#feMxG_*lD+~ z2j*VpwI=|AB9A}a#C9Ym1zag<3?EXsylaL8t`JK z*;^KTO)Hz>ZZAR zpII`dpssz67+*Ow2^x<+_Eqzf9DGr9{pB1L*j+sNJWjFf93wo!egJ)a2wx2LZdE)% z)qTE8M9_q*#nDW5Nl62BoQ zuwT@^;ukfI%?+`{USR#JzbE=j9Uxtp|1N}-#UTJ6>G`H95HgY`m&XLvVx#f*?*)f8 z&&!M)wr8&!F2CvNgR?!2r2w~T1xUk7=^LM5xbwZY0)8A#o?Vr_K#i2c~ zQToJdar$%8#^}vqil+6hLP(va-wv9c4FVxWX6Hn%6KsX|-2GsHGkdprYT0~Yo=&Wd ztY%m~wiWH8X25f6OHzQ0{Y(P{4dbWz=z*Y@DQaJg3^oP|9nlubo&>q`vYOy{^b52G zdiLIbiYxooHnzse*tXYOCkX+@tBPdtzx}Yk<-7Ue75RUP@hw>U`iB+-Ika05xKJ=G zC~xc>&U@>2-4+Lyf=#pb2ZK-9w!dBx_q&S>+4@c};{k>mQ8NA)sT*Eg3#>ss`xd-> zxp*EAs@ox(NX^$1$pZMWb$3m#SQg(gUQw2mwRmWln_eq2QXAV;f3R&46ae7^M`|vN3$a4?NV=C06aOmsX#$^ zo3;t*G>5f;EJuQ;+>Y;s-3zN?Kn-t@D#|Ga+yC|n;50$&;eFKTeu`c5V%oOORCsYp zdr4fLCKOq9q2E4>M9d{_^hJV2id$LE{N&sZO_DzH3^wx4OQZeCk3@t@BpQF~b!M>? zi>48H@TuOXNSMs{_7c8aJus?(dO8V_Gzz{FGHGn>BWvuaN2POOhCC3Qig~89n-A|~ zb}l^Hb!d+!gkHE8Jp!L{?!X;=XStjkqevb(Qrh--7V7J62x{5+GFx+X`OI=1$>!Gl zO)}4`nZqv5-CP4Ga(qR}mW5ZU=@KXO<_1xPZ|&`Q);-)&k(^I9ZnfaioM{SesGh<) zHsZI(P_aVHg>?+$y|X}axkdE@x1ed4D)pT%7nl2B&TEq8Vnhdk%WFlCA$E#>v3O9| z3ED?Dw2}&NtkBl~tPC&peZ^@s-LOPnM}2n#wLjw94jCSWt?lO51qcEiR|?tG-@G-Q zQo`2}zc8<59nPQKlc&Ui3&k8`gsuHBM*Ku47!f{u_2hThEb}g{+}2o8sn3faDWqxm z9R5=z#rYLGM_X`Vrjk>rAWk}i@UjG}3bhyJ+f#=|wP3KQx~B-$a-d%b1*5{^{U|w6`PiZ8=}8pPXW92cyZQ zas@NPAxr)b#`M4M#P7V+80;M7&7LbzPX%h*p_QBSuW;hX3WkI5J8u+!3H%rre^7d< z@GIUFAzsYihQV#U9KMG*G@e@L3+Y#G1U=4v9^T`tI;+!ms!X6DAaCrIa`HC6-Zo@a zhko)FwWa^m>oV} z3Wlz2Y3gY}Z4kunPU)ip%309&c_^vUcAt64SUP1T-h3X-Q# zI46o=AEk7IO(!&;+65gsu;x7`5wyXOb<~X@0^~xK!RRzf>o7PUv%ibow5*2>aR%He zyHj*Rud{9@V%)O|wbzggh=OCwo~8YyU)Lv__v@O(~!6=B)88@L;m{`iojl&6>39cP$|F z>^X%`xVIsf8frfu4%vTH0-eIZGkJ($AquOH9qkh$xr;q;0F!o(>)#9Qc3DthzYnCN zhsjqN5=nLf6KhJ%HN6+WBAfJkFnK;2dK_qO9cJQHMJR0UI@4SlIJFJ8No zUFP~Sm8K)03$M&4W1o0NM9zZc1d?-K+^{H0YE|zexyWKO3Qsk0cpAuv`Meyf zTruyX>t7T<;3}#AbH@15!9-y5r()}`%3qJS8~=*bgziaxLWO`B>U%QUxj0vkq*fBA zz#_+JBMl-z7EI?i2r4niJ6OTxv8K z`=~f;V5R6`L~+XVF#$o~Y|u7#Qv12`h1E6L0h`CTtR$zri#fIX0;hNTWfvwcfnYP7 z_u0vp@A`0nM?@#?+y5gK#OeCmiD;EN!?0w$H~$`M3AqpfrqTP<$UE%aETA{)MB5dI z5pG15+fhrMT}cFM!7Kcoza8Vrr4gx%X`?yM9_x?@ z5HNq-6qN_1yFiLY+X0xC^yqr_D-2*&g(P-mt;F~<&xaPts2abbjT4eh{J_xvyns=s z8#c-1YKpeKGO4>=&yEj?`JIGJdEV1m2|dw8mXgTEx9>4oSu|hA!E`&u_?^v9{@woH z>#c<~F(`)*drRx2dF+wTSIj&JJftK-d*Wzbf_E>d=6UC6zx~GwI$%o@i2}b9n05cpy;h`N#upKBDAdc+@5j8|s+E|$)O66-fPV$XTsT-s{hce$5iw9go-LU<9C{g70|T~1kt8rdys#8VdhN89 zM95}X{FIA%^z1lb_=%sSjvudkl*nv=q!tF(@tpgT&+%`$p;7uJ1yo&t)Aqdg+J_Ep zyR|gYwD;#tm9CKpm%y3miYZZr2zIW7=<|*tnuL|VzFm^Z>5KG@%c787hOqZ@%VQ`ccW??)Rv-Lqd`F)%O)8b@n0ba(58CY}V~2~Eh@ASuZF&hpB4{kn5uYn(0Sgw8b&;oHoZT@?1)y2KsdGsR z6q@1g2{xOLzv*sGit30-wq;*QSUWAp@i#Qtb!|Z-Zu7@E_I;vTp=^^>fh*q2i#mY+ zkJ{puL7HFjl?7$C>aU$5E^@xP_vw7Xnp+J6&+vC6T;W1uUA-~7T5=6QOT$I3t%XW$p?~j6oj%7pLq^wwNhcjO(v!ZV$qHi!7@sArdsx@* zf+a`!^X_Ea_ud3oBifFDcSoQ7j`7pOT*_>~YOg0>nrYWw$~Pq24ptuoqOkG`0MjIO zJ+}h{&Z>aUFt#4tsTn_aZOtQI3^h`KEiRY_oY1qS!!f$a69~>_xKgo@XHLeP$0@o4 zlUAYxa~}*?WbgI_Bc5(~wr-<%|7I|j73LneLrT8uxdc`R6cvI|*jD!(e0TMu-0Io> zZy*yw@#{H{CCQ&AP%UK||M>Wwi12*+ZmC#dm()O(YBqM4+60@nhW*HB|1ivt+7R>l zeH2^)w{8V^o{0;#y;6OvYm=NCL~S-nR<&wjn0^E}H3O*)>Cq?3e76X^@Ju4+wX;sFf zW2SOCy2izM_r|m5hy$i8j-o_t%)oMbNeeads|0WG)C1@r10h2b$yh?~aVn;YM$+#( zlOtK{n_^7R%~Zc*242VKhe?DiOqjSjL8_C$1;#nxB6*j7;TAY0H#m+Zbxq8~!5uZC zE?pwdLWxtUoKgnj?>rdk1#6EsLkcR~K$}X3hQUK(HioFa&847@$T<#40YmPa4d(%B zW9+n^y+(iRXd~>@X;c=2b3xBqRYUx4`JbBmoMAbJHUeK$<~!~MF``p3n#6KGhvFr1 zI|N_2J(`4g1~R$!`7FSuM(XkqB0Y7gBQ(LL4+)EKj4b0r;=ItiM)Ui=;k z8#at@Y*%Y?-{f=1v9t-DX_Zj-OVcZd&(lPWFYCnGodVJ>aU#P6d}BDmi|Cz8KuZ(N zS8f{_Od6I%+!Ek39bbzg!|SGwhT#gBsjcDpp|CJL+#ObO(2aat1W%`gaVG{1E*@#j zz;8>y9$bpdtrzyg)!dsvtK(qjvqywn?y?ROV-nq9+z7*tr_tr(+#Q~W)u7WV@`Chm zF1nP0C%o{<-az5@6nUfpd!HhC@i^j~$`iy97$o3MSJh>%W{d;Zi0GAh5%Qe z7NDZZUV9N@OjGtnSYwxIedHo5fOZl@7J!Ev7A2OpfjU?G&=4P->)jXj^8g#CfGn5R zNYSed=aDoI*AzNAOXICB&DzWEqf$k|%qFaLDOVtYduGYTbcU!L!D4R`-Cw7Q1>0fx*OAc}O^BHcp zHor1aWD%41Bog-~34ms=;rGS;^KvR2Z-Y)KFRT9u{`=u`HpYaK8?T6r$wr~q(R#v`Fc za@5Q!XZK|Q$YzFT<2^|7uGX>H=UUukmX6%T*x<0Tw{2>9+$SKwyUtJdOg$U2pM`F` zmf2;tohqr3as4XtEZAD#+5TQ>KX@W25YZ4q?ekhwcmKdGPSv8?z23s3e`oR`4sQ&> z;rTu*!vL+#n|oWxn|qm+m@A5}en%>skb0t*`K(aI)OM&e^M6F0YK5TI4bXN;J~p@{ z37-WT_x{cB#0k6s$i>c^ml_Zxo^Ka+%H&%a+kHnnaW!(ty$Inxl&{>Rs7Gsu604-B zQ^4cnT&Lia**Vp?mXT7O+oFH;uD?r9^T74?(3{<`pzcPmiF?l8eAIvwj@DLZF>|8O zu2W;`v-cw}pWm&ruI3*Wcc=87qb5owK-KQ~)((iuPd-IER`>*N^wKLHY39~>a>ngW znK?)C8`Q)B!1}b4l!<3;&1<8Z52mP-h4UcT4&GPwYGJRa?tt@(IK@D8vAHS z*}mQh)yQxx^iMIXvSkNzJ%MX-6^jpA`h6U&G4Zn(q?Gv&B}v06h%fhZ-IJ7t z*FQyHJPbbNcd&K@x^CBHYRsqm5Zq7V-_ad;;41OPR*u;Tb-(A&D-V(P-gvQe-md*b zo>f1>Wjx6U4$pq8Dxo(F;&U`*m1g2u?K`e~@Aw34?cfK)&hPo4nAZ|1S zd&dOPjIrQp3+F=qEC1>8^^*JqZSu0@qLYBBBkwsQwvE2NZ{Fm0v#6t(u1PZD!a>Z$ zQDGEUkNt!fRebPtY8^EM_)}kZ&(gEH{EUH>Lq$3JgjY!aX=-3$v_Cwg`$gORfUDQx z!8+ZI;Yv#|DDca}ypXr{W!I>(a9QI)ba$g_Ucg@-FC}4Z3`O>{FOm zr^Rh6Opfe-$YZd`;T_H=Zzx0LFJf8+;Y|Yn?UHtC(ooZtm*sapNqixnZ&*1N0P?+h zj_aX{eoo?70IhtUOY_wUEol3$H(-B3E!L=1f`y(TZZj4MC#v+bPA~CjRJ4_1g*`D#mvvMXAG`3M7N7o&zt>~ z!<92Hua`_R@ux;q@Y;tC@i^eLJ6_)4m78Tz%B z_;OV#;e$Vo1-69Jo8Dr z(Bq&Hg;P~hFBK2E0cgFZ>Yj|0j~6G2%a3$HPQq}$G;6nm)!bsn{Bn@9C!#p>ortMEWY35_Dg5QTb{WW zWHA0`fr7HyA|BB%VYlN|5q%w}0WF}G9herA9||8511t2qY!g8d;DKu3x(?7{QR*BR zc*c$nIL9`z-AD}{N={Dz{1M&m?=NI8+Q6(h{cp}2P)@me!2okA#ShK7=E@wv9DU1+ z3JPN_ZfG}3Qra8oI>G01#>H#vNHHl+UP;@@<`L8q#0lRN`GB zC5dm_=8`CQRZ;mtIS^J3VC0q+E|?AgE%`m9q*431PjA87I^g4VXZz;!;Avvy z*%QbQ_9}|(CMN;z0b55SifH<$faccUeV34dFxUZ&Wityn95NHKklq$Gl5zfvXvvCX zRIq3X7Z4lWx4Toa>*k$B8TT`lR!@)LHk)4%twQGh18_Ic{qwH#Kfh&g1J|UB{pEpV z(BFZ?NcE^ZV`XmOfTAG)=`Kngy1>f_@KGd10#3#Zc-B-}BnJgQ5bX+%Z8|+FEeQ(9 zg1}_m%{#IbUUv@Qe+PyEgBEf*{%X|K6qP{cUASBWWq!axjn{Uq?6ra#;lj>=*8Ii_#NTytingESp>7RA@Mj!=Tu*U! zL%xtk$xyqP{-~$=ynmxV9YEeuOt=K$?h))~eyCf>L~ruLoJ$8ZwJ9#{#}YK2^gOzZ ze}BRKf-7Q!{lay0D4o75CPveEp3ITHr&YarlmY!5rrDng@={7L+_KZie{0gu_|UC} zX;`liRh0g|g#}1Y#rv=D`>~=~Dx@v6s@IO#uunsXdvoQ<>=0lDe(E;KHl8Vo=G*On zG-cx~=Vav7&tYew;iq>|D F{XZ5cG(P|U literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/airplane.png b/app/www/img/carimg/airplane.png new file mode 100644 index 0000000000000000000000000000000000000000..73e764c5c94aeea5cc6b5af8c8f0a4d3d5bc72c2 GIT binary patch literal 6880 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 Z1~xqMFZD#t`N7J7%l5t73b*3U{Wsd+1qA>A literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/auto-ricksaw.png b/app/www/img/carimg/auto-ricksaw.png new file mode 100644 index 0000000000000000000000000000000000000000..bccbbb5e6e2251bdfb2cece8b132ac8b7a90cf65 GIT binary patch literal 11888 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
-{5?f#sw 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 WdW6RjU$kFPm^9V&REw3Z@B9xLurlZX literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/bike.png b/app/www/img/carimg/bike.png new file mode 100644 index 0000000000000000000000000000000000000000..b7161ff1d1fbbe5744b5112b0f347d4d4d7c3027 GIT binary patch literal 7945 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 literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/boat-1.png b/app/www/img/carimg/boat-1.png new file mode 100644 index 0000000000000000000000000000000000000000..ebc556e9f6cca124bee063e702adfbbb092cd209 GIT binary patch literal 14793 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 MPEEE%+BoR{0*+p$IsgCw literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/boat-2.png b/app/www/img/carimg/boat-2.png new file mode 100644 index 0000000000000000000000000000000000000000..54c52b449cba1428417d82f1492b431c06facf6f GIT binary patch literal 13772 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 literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/boat.png b/app/www/img/carimg/boat.png new file mode 100644 index 0000000000000000000000000000000000000000..ed707ff5e68be265724d2e00898792f2896b93c2 GIT binary patch literal 12519 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 literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/bus.png b/app/www/img/carimg/bus.png new file mode 100644 index 0000000000000000000000000000000000000000..4cb5d953c1f6fa8bbe4ed602a8d3f43dc40f707e GIT binary patch literal 5759 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 literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/catamaran.png b/app/www/img/carimg/catamaran.png new file mode 100644 index 0000000000000000000000000000000000000000..77fd3bd3e648ab559d16defad8a636668fd520df GIT binary patch literal 11066 zcmb7p2UJtr*6t1j5+P=g<5!9DHVRzG>tJ%3~-W$hGsS-XM|JUDMEA-^t3| z>xru+;N|5dW$S3?27lsgDdps9ow}^b2LKlvA%l{aqkq;9Pq(9 z^nNcCh3Y!~WA>%y%U*YbRg{P@BV--Bi_wd@o$$A&bixC2`c^bAGmy2fKxX?FaSvp1 zlb0D$>(Q z$m#<&$m&ZfYaB<$w6^iliAd4=e>Z#q07K%yffrjULSdW@jI~WEu$!pN&;;et;RJ%f zcmpB;00Ec*4N4~dTapNMo(6SV5IBp1Awq@F_)pN;a6oZIubo{cK?Dfup!>ZiW^l?& z-8}7$e@hZ*-}=!YXM=UysiSnj4{_kTY^y=EBr_mV2r#ewZOms5`fmo|Ob`p>g;R$; zOZZks6dwcd+5mVtE{d?Ly9xk4M>M=;)WcvQ;}C4*CpW`G~{rz%YPv=D^D}v(r@G?+nmijZY9x z_N_gd%x?Ja2D7tN2+l{lB_dR@p8e?ODn9udRND+w>}R&B6o?_B)T&(v#l^ld;1a(|y3vzyq0W%HSA)!~XtxSs==$oDym_ zY53a)UMO>Dpg$DMu}CAp>?O9~lTVAZV!s_5s7V^&K8IM@Vx+n?p`J(wT1+^iq1a=` zPUVj2G)#LiK>#|6r#s%GSHa2vCw)IK^P?0DXcVCpUw=R;8!#>tx`j|?FtQ*5wpjqI zNIFofw;bWE4_KBA6c^7WF;Qht13CeBE*;8NwVE8tGN-0;9P;)47M2s!pr`}u=^KuL zCfj7(YTXIu7gP;aT#OHdf*S}aQYGY8Q^%l=f-4HtdD1}I5h`voz-*8te2DA9{aYNz zOxGkrL!S9Frbuf5JPe!wFJn+C7_eZ_`aU4M6d<>ofH9*+ATe2-vdE@S10W0{MVk7+ z1w46205e1}C@A#B@6j zj5jA`2`G+^P_;Dzt8X+Y3;Xi1Am=OsxDqJ%tKok}oCiMzu2IZ_iltnq+F|`k=_N%f z<6u;k#<5P-rgHFG)9-O7sA>m?OlUlV6df9`e)=TtB$dZ&seXoHZ{|X$7)A5CYllE7m#8s`g+iz;fA0uAOw(oW#!3~w ztx!qD?QWZv#hpGhj6@Tj>X4=)yCo7rsP4ovnRN?Xr@BhEiJ>V8=#Zfay;2ILMkeDD znu@r=`uqT;N|R1awWg`PyK#)FHq9x6Hr@P?V_o1wot~lYP(b4#6fiW)%y4{%9v^ay zJLG6VX*Qw3x5u${C{Rfo4>#DT3jEvsX}q(YRE{baCq>}$rLjYLN}Nafyq(|qXwqI z>5%jO52x;sqbqj^Z*&M!{o_z^P|sfhyUAd0k$~MG|GAd1t=51 zF=fe7WdHq0n@UHM6rmz%fXDzO2$TfHz;A#7{QOU({}0vRWJ+_OY-%9ZC1!9c4Vprs zIAOm^)6M{D<2VB0x83VPLN}SXv{^`*#5+8dr8*S4zMxJnhL;O@{HU#fw$k23XtSC+&Ny7 zJw2yZP%_p_SAm}Ue&ZxmPVbA&7n-~&^C61cc~_YM*v>Md_dBa3s>k1D(I}V`C~H~e zkI?%RViSVofmvt@1P}bh0NkluV2e~JVZOf5B4Z{a6 z=@2oam)Rl0thdow<;)NborX>HU&r!EW`H^SOCakAYOH9NmHUZJx*}t~Ca8K#rb%0} zrRQI<5cOwlAZQJcRJXcdD+9B-8u|&wjn0q*K_VNEdQT{_UcRz6icrSosWAg>mtGC< ztJ(fMWBUYV;}RMQ<3Ym&+y;agfEmJrrVpa#w^;l8pJs&c&0mc9^2H^}$PSFiUQ`qI zf=+|7Rqwmw@5Q9CagU64!ym^!wV9b0oTz@vXau- zWXE6;{l2g0CaCdAPQZQdQ=?glscS4!c)(e23v1X7dk$pv$&=r-_Hp~p>I^BtRqT&F060k9k+H73_RD-I0}hcBcKfQK>5VG>!+=<%D-&Dl2JZM}z8hrs^$(QFyw%OC0rbqr?v2{XgeFvBv>A>AWfuYVQs(PAf)C9$gw~)Wa)o9MoPc^oe&AUme+ID80!ZIpuzp-O zwmV4|q~&yAUWvVSVZT)FC`98d{cj6a$+x{n2F^18(L*HiP&)bUd?Cw$ogKK(D3GrP zY8idy1g`uA6qGkz`(kRBe+{_1(n*H5s|yDt2r50h^Sl8*yuqr2Jp0@h0)U@VYibv2~o za_3M8kTuXrj^x(2(J?Oz1(8Jo_TtyqjFnsXLV>IVpbB&CsKcC@9S>d5H^+la-2|hX z*^2II2Hw6Gp_vx3k4o$rIHz2{aMeKl`DoS6n#1a z@Z1FK9oAmP6qU=dL7Ft^e*3eExxd`Vw`B(QY6*pjbrusZV>r?4z60_Mz&=Zmli`)@ z_x{a8pcPPv^SdJ_mD^q&ve1%AJ@Svi9sO@+)-zA%nC-Fv zkriM!J%qDlQw4m;Vp;ZYz={w|lG84G96+NLEw>{+Joc2aC$%Hl#d=ypb1+yFa#MsM zeOrZ;sBoz@bC51;P;}gj^_tb^qDaR$?%r=wg#G(&^(6lr_rP_ZvLM!IVjs8YJhg0O zU%(151OdOhm%R(lZ&%yCxl_`-2*jUoGY2gT76Dv6`((u0Z>>3DDmhL zP8{Tv^%%=EVWhM*$m@LJgI)|7H$Ut5hw{-lQ!=7h-~MIwkXzfk$qKvx6K%5FT64p` zv{$0~q%q`35NP!D&T6K)WI2n~aurck@GhvELBo@SJUHBpD5)$Aqi3OPvOg9lps{<~ z&|D5CU;hyJHL8@9?)s{5uI`$%2!yiD6Lw?5$ag(@@`c#{2AeZoQ#0bn#)L*+fOr92#ed8@nJsjDKZO``()SdbfW*c57(I-Zk$G?nXy+(peb; z-3FHoxHdI-wfsI zE6a~7^sQg%Y&^{$?fU_{o|O~1gjk=;N-^buPJHB;TTsS`BOitbXV~xA{7L^4@*j=Fxr_sM zlEE;We|~wj?z*7IOs)LdLB@AII2W87e%fos$!E0>dCvAW89sac4nnOvW4;fdC8-M{ z)?vRF`V${;WKM9#il~0ie!rdMoF|;Mu?VMLw>u|6%7e8KZYL=LfR)_h#ehdGH>Q`#jY5~Eav~@Ee^V@agxmqa9zJVNv zObAoex8DfHi+feBc&{&7;QP$;(=H0Xksq`QOuR}mUGqYX3qr4xB-Uh+-P}+(DQ``- za`J7!SrW;BRY0F+C2}ICdp-)_`MuA?zapRS%JJRG4|>nOVLUFr^yu2Xs{CG*K+fP4 zuG%4R*MDYc&l6o&_0Jx8DicZOChL-F*T!2A{0J?Q(V8_XQxKu;IJFmCYNhgtfwC^k zBA0f2Bx<80P}sR)J;g&}u=uk1)2Vj_1%!Gr2I?X|UoUKa3H?daG@3{-K_^hxYzhC` z=$yRd{4>mo+7k8zrMB-{DX7-SD=A4zICP1s4xiocx13wp=$AILD z;sDk5x1C-wnYMB7vf0%MN6;x$o9?TTthM~qNMYM$zn1${4<{#^IOF~KPu+28tzS{X z(Iz8z#JWn9exB0KAe6v$saZ6#uS^Uh_Xu3y-Z^ZBADsy++(St=xnIe{hb^aUB(`g3 zRXAklrJGy?Yxa$`L)?_)Ds5$JZ3nMSiO+eo^#}L()lTJ~4*0IF>fo8<{#IHiDhoNEf z+Vm;Jx}T5FG+8|Kh0hUtRd2}sJf`_M`_>;ZkNsfK78bBOCT)fv_FS8!lTu&|+iXd{ z{y6f)6S~fu%J#7g^YahsjH9oGPw`JFaJrI%Dk*z1VeSuWS$6^4wAz-e6&jWdxv16T zM2y{_keW{ZDH+pIdAMcq;CXPP8L1fQJoLE#S9K*A+@*={wRjaPYQX#3vva^LY-wV( z&syn_gmlv^Udo6aGO=M*SzMqxc%>6OdK}aPpPN0l%YN$m)c9kh5iaILu;k)KkLc{9 zO7=5I-ZKcvi=!^?aODaozem7y3I6GEU@ipvmrvN+BT6!H31^Vyc~WzrcG4snH0;d73q@D{)?D{o=Ym{PJSptm_&El?)@&|yVfp;e?uz#3OB<`1PHc!O z9+%ePuc#8cC&e@0^oD;h*+$Z5=0-hnO z0pKBJY{bu-=|9-XC}as5sN>AMW#z6npPTcq#^V6JTWfQBq>Nn*T;{-Q#_;a~V&A_i zd__8XWX+A?0@~&~X2o-6vBNz~HhpSB;-2-}^>QfJ#8UIZD3{ufkb-}y3Y?cGl@GK| zeh*7v4SK$!gwP*_KN4=o2CJV1Qi#|5uT*<2-7@vCUOx`$fnwB)-(lhZ#7-He?c`WW zk_j=5c>zKPrOj)lz5?hsiJju|>w=8?6+eGyN4#nD&OsNCtbIrv#_cE)axun%q!z95 zKE;?jXwt9PLQY_R+)e%dsIf0!H#SoJB9Kx^FvqmB{#;rw(bV9j{~bCf9X{V}ok}`8G&KPo-k%L=z`_P}Ik`Y5EQv6H;+v8aS8hclE7Y z$yraPg>n@O@Pl~^HYFzF$qFrvoZgx3*n+eJsTeTwS5i>8@iEI?R$_}GyUj=bEB)rer zC*WEGpM|wUtZ&;MG(VT}cJ`S`T3S(DP9At6E@XTLP_M)L*VTj_duoOf+G2s+9X~y( z-|ED)d(FL8j35pvYU7FpkK<`2HkZKsZH3{qngY=Y#0ltiRJTySB1#R?AojSt6H%${ zSC4Cyx_++i{yaZDhvE|}{n%u;gQ~^r3Y**7BTVjtWixcYyZyS%AMV6_>uAS*RTl;3 zH?H|B`8<6seWMb3&iscO(1ma|DpVkC8Tc#zj=`s_e!S7$(u@$2TJo4r*F%(lauEPe z8b}-wn!Jx|91PUseqWGe&}YcLKRD7Gc*8mJXY8p;g^3A`wc=5hoksIMMbthW{=mg( z8^YCye!QWf64R|0%q3cvGVr-&xmQ_$$!OfrSv~s!>7N_%hUnD zHxo;jA4`#u%CYd`mCneEi6{BC+=MS@`+NC11?-l_cW+_*B)rt&j>ZA4cX+$9HlJ>8 zu|_z=_!qc0u7G>tvsuyl2K821L#FYHs@ar=%c(%PT6T@X6^riVyQtVtiI;(1jeXPg zRA0SWp=H)xlN=@u|Cdf83PiRCBQ~eTMGSYG*!&C5R}8*a#r&8W-VaS%|K}_oKEvm2 zH2!U1>n&nWkjbyUvC&i6Lw80_yff#D6`$tM4{PMQl1RD>IB+Q56H1p9Z^>cta$vGq^MJ^k&?p<A+~T| zK;@(8lp$#vqqE@Z)P%^8tvzL~$#1SU9N`kh#Dbg&)g2^FdA%C{!UeBxS1LT6x;(g7 zXmPN1#BJU+9~x8qC<>l;!6oiV+%g-sh{dl<>sNUr*A(G_k&qc?KANm^%OKM1Ww~jp ze}gH#i0%%;Z~k3EEZoF(7kt^|s>H0`dY;XsI@s%#yB6QDRil*8-7Mu@inSl#-v6LM zT3k~p^A(sXk6N*_nMsa6KAx^VJ9Q@Jqv9t%AEm;^?Z%H65|7B+|IXMT%)`t>)x@?N zq%BVabBPC5TVGEm#!rXLZU?$PTt^I_0VV|z{`UB+VeZqS{OGnjFLKmY16|sqo~IwL z+K$cl+0|41?u9;Se$Yd2(K7}YP8}~XT>@_#?sZnRtXY%WvT6xpR<4{Dc zYa+2SG=62_&6IUSreq+hS9o}?JVx(~eS^7McR`xFC0PhuW&a4s`rW=jPK%N|FU8hR zfPdp3uM!x(%^Kn-un-}98`ZhE!nX&L@KmW=TX9$5B^nlw{*={m@%1g*VP5p^U9^a# z&v-UhdSY@VCi6^S!XwkE!iTCKE(lGRwFUA5*Z8XhU-mbU%dz-MTEFU2a^ELu4JRuxyr{7# z{}pB6lWKj*`c|#>C(S22t%7nf$ZRi(g0!)^!1A0oa_bY&HAiV7kv{H~yG-6Nm9EDv zf-9X>DDV}5@@A5-=6dUnwV_l%&Sh;r4=WKNcK40Ac8oWlwSUxhk-J3HT$W2z`TURC zga-o!hRu9}Kry$N@j3iCX)kPsY)(z0V_!HE+pn5I#AO1a_4zO1b&l3|?0yj6(o2K@ z*Q@L4Pn#Pul!^*~@YrQ!w7<(~g#XLJV+xyc8-dTg@f-C84~0h~R92aY`^(2U*Nir* zHdG6CmR**GNND z+N|9*n}J^OmcNcLGbYA+HZ!?8o)}EAnfsW?k*x^$z={%5&wlT&)|w)~dfM zo5?sr)uy+Ri5aeBB9f)i2qv)c^<#YZKp?pbiS%+ZHdzdq`U_sUoj#1IHyCsdO($!~ zY<6HG(sfhyk16?h7B^QVMl8kn6uagGsz!;8y<%5eC~uo1c{3S=*6!$dwf>Iq5|15> z=HHk!X7Wl|{l^kMd>|(MsD&&{mN=-~^_m=yCZn(cyL}8YXo~`5T4eQUw@Ww+OWjY^ zylz);&pHN^YGuOs*OuNaNe$E)5-r%G$}bJH81loTRd*|pr;#buLm6%c9(Mv7YEXR5 za*ejmRv!M#W)9u2)QC;K3{6*pj{y!dwh-{*UIU>1R0O`}FaY3-6NLnTZ(o$#dVwmFU}76THow~*7)N$pFLc_tBl0RC#UOdRi9lU zeXqO-uU?Sbh}OR2(wN0_c_Q8dFN@=g^xsbFVh=Jr<*69>sCd6v+UW0Rr&1qA!LbDQ z$d;^e%a7g>;ki*WU{=7xB)Y1?@OQfXkvqjA#!}*T*O5G1gIxnlnfMnrHKBiF&Svl?WZex zyOu!%{AD5=nmRni`ctyNGPP*mFR^^;mGq8sO#FgLVs+&^1SWTU(M^{<`I0unrrXB& zT)$F4%tt?QM$i13N|z}3_~4g=ap7P1jJWL&C$u!ej%Z-Q^oqAXxt8iAz8c1;kQ?O5 z98sw=?h_)q#oIg(m*9`gvl}I5BcBH{iv>J0bRI0!Bg~mc~fVZ)++=)va9d7+JXSy`Z0{*GSvH1m=w++F?nFel2dMvJ+Q;9; zv7J_C5I=3Txy=z|c%JA7D=J(l-RSG$kLiyT#&9!j};}iXCrQA7n(Er#vpgAV?6vF_y^`OH6``gl8$Mr3tKf& zQ%oVLbGD0Z^(V!;3%U(Ic|CL4c-&>aesU+NA1*N^*SZDs98rBmH^hJ`fGqMFtI zn47LctL3abnAnHr+QZ>qhV^%r+wiYyl{ZYu8Y^b`Tcg)<%(`Qa6dhkF@@37fdeHYu zm@8`hi!1Ll6F1z`I;->=A7rt7kKXNm_t1;^qJlp1S$^G04Y&NdT<(m!4V?uhYjoa^ zb8T)F=fzYL%k8etoU6Ced=R@*r##*9kI915X6=iHWrMj7`h#n={R=hokGr-#+7d@w zin-8+;wYKrU*Y=G>8@6y1EliCfl4EbLc895n;G_?&uv;JtO5^aGGr?Ap%*xFh<5d< zHD=b2E-F-bl&`4#YTFl^f4?dJeJR0THFVqLj}O zbiPpWBP_J9FBs$a($(1 zT&|nWC|elV>D@thb3%1gT5OyYiclst9$6@v<7h24X!rP{AA^NMgk0{g9hL{+Vza8$ zjM}cie^4uB_tRyi`yH$~QGP$8CU>jM({iG|PKkAeFY@GCVeU1(voKDSY{yYi)yyV8?gzFSyiPEFd5!hYGfo zv6IoUe8@@u6_IBVFA5%J%Ed}Ay^4ql`D{9#&Tcgk?N#QI<2`bPJJLdO=kdQUULSQO zMYT`w>bjl7>2_o%>BKwnF6KIv#yUqR3Axpqc89ZX_dd+CEv`WF8MJo}s4Xj@tuKqk z@~>v-`vJ$)Hp&aq^)iXxDP_`&n|(@!9Lblg17XYwA6V-CN@P*(>6BRZk4ep%REGOM zzx~$Mq%<}d+9lFb`W}8f{Iu+t$MogrOv-~(aAg^-Voo&OiIY%N(d7i&Co#p*2qCgx zl#gf%inlgnOtS@NnjB|4hO`<}6BCE~Y%jzk9wo=kjlV)v_Kn^8wJ`QP2_8RH>!*ua z3+S3h?_}bqbEo_~<((oJ_ZTk@H1NEC=;rEZ=|K9ut6uBtpCeaxS!`;z>wF4h6+Z7K zda)Oq3Xj#DB3LXA+L9b|m4IIJOPwP08pb}b|2$J@{Ff(+!^6h^eWsuuGZ;_)zfL54 coB&x^gLrQ4{(*of0RG+5)V)z~?eUBM1JwhR4gdfE literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/chairlift.png b/app/www/img/carimg/chairlift.png new file mode 100644 index 0000000000000000000000000000000000000000..f81f7e0a3d27a8fbf4af2dd8d77dbf6f8767a3fd GIT binary patch literal 8409 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 literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/double-decker-bus.png b/app/www/img/carimg/double-decker-bus.png new file mode 100644 index 0000000000000000000000000000000000000000..9c79098fdcb03afc218efb393fb824e03de2501d GIT binary patch literal 6219 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 KB_2Qj^Zx)+ncubm literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/helicopter.png b/app/www/img/carimg/helicopter.png new file mode 100644 index 0000000000000000000000000000000000000000..05a018b709153a3b9dfa46915b532bd3594c9e26 GIT binary patch literal 10790 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 literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/hot-air-balloon.png b/app/www/img/carimg/hot-air-balloon.png new file mode 100644 index 0000000000000000000000000000000000000000..a05a93d14a02906a3af78f72778fc904d43c9305 GIT binary patch literal 12310 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 literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/house-boat.png b/app/www/img/carimg/house-boat.png new file mode 100644 index 0000000000000000000000000000000000000000..23d4a2013e629dcb6cbb5d48cdd6098ccbece2ef GIT binary patch literal 9701 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 literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/jet-boating.png b/app/www/img/carimg/jet-boating.png new file mode 100644 index 0000000000000000000000000000000000000000..429a43e9ab520c10a90d4e312a05375569e6449d GIT binary patch literal 13481 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 literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/locomotive.png b/app/www/img/carimg/locomotive.png new file mode 100644 index 0000000000000000000000000000000000000000..df725bc59e528995c485a6fd13c2624c69b4fbbc GIT binary patch literal 12344 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 TGhH+u2rNu(zJGGm>*s$1VlUY= literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/minibus.png b/app/www/img/carimg/minibus.png new file mode 100644 index 0000000000000000000000000000000000000000..674cf052c4e1521d435b8d07cea348c846c262a9 GIT binary patch literal 5625 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 RDi?r%x>{#7bN{jp{y%{>V}<|# literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/monorail.png b/app/www/img/carimg/monorail.png new file mode 100644 index 0000000000000000000000000000000000000000..634699e8e21e4a0c692d9a805561f970809cef0a GIT binary patch literal 8348 zcmb_?2UJsAx9&;-LK6`IDMGB+LFrNw@X(Y4{30DCdNd%?1nDIkIaoji#Re$BauiVn zMS4jD6;TjSkWLT~k={!n$z40>Eo0m<-v8csjzP-WYt8z7bI!fvMH?$KaS?eD0D!oK z`5y-Xz`~DMKoEp)(;gyoD&Vi9ekXSM`n#u&>dON_l(YE5=uk-8Nax{8uI{1CiQpjj zppn3@Ryk5h64!`EUu2BiuM4irl<|n)_QxB64C9KyoyI1I$`H}Uc-KR&ugvXYZJNRl z8U1rZ#K`mHzrNhR7WZbe@B;DgO#C&SjX{dE=Odj91L5y-*IgM23I0)26Uv&KvwiF` zgAH=;*Z)DPyCYX17-K}4UGePM=oJ!Ir>jfWSG%hH&j)nvcP{R$cz4Yhi>|&it+rF6 zkz2lecDWh87&Ug&q86D6318QhmQYanfX<39pm7GPZdw)3xo2+z)UeMi1=^BDX{1J% z;Ywf8FH2*jNw>P#+`7AyWb(bBu9u3>c^sEV9Jdk~-?^%G?Vb_(F|pF&t1H2#yYsdE9Kagaw5f|)6A%Q`#S_| z^%fk4xV}~Gw^IEkv&Gmw68pcV%jM8C6c`36HwcKA6?ZI-q!d>`@*BrdnbLOZllK~G z{s0?klMEGZ2(hcHtuTh60@4S7N55DG^*ioC>qTHb+eZstnwSY`Z+h=mF9<$-?U1vn zt;tTHZov|Zl4yY^N3?sr-TxYL1Bf^|z%F+x^?_3yFz@eUDAFRfYxEr>VZg?{xJ)^A zR)eJp*m#ev^YQMW@=EYmNq~l}B)CkQp_vtqJMsE+G04k*)NzIeDOm)>bn)Qr`rtWP zXCCt*mAu!5`N<=HJ&<1<nJF$IvY5QZ_a5)y+E3e`iVId;vcKf`)`Z)@qvt8_ z@DUEK+QL^f=VQ!fAV2r!Qo*JsiH&&}^P#uQgZouq;mNv&R~wB~0ixmlT1DRzn|`wE zDI0^Kd=){?Dmpr4mA~|-9b~)`2R0UCZ<*YN8WW&V;hm|RgHa0*Acy({*W<9v8foyo zXj2iZMH7(CXT#E6^!>5vTW?;|8}J6m8TZ%M%&dUM+p{rB<_A-9DX(!VAm!NZA}}rk z5=Y~sO%m<^n+IBe>8L8mralMh8S>JAIsO(&-VU~$!gcQi>Bn$Hu;r;~nE=?RFX{z6 z>w{!h7h|?)fgYB@N|LMTQ#+0-hSDzr_GC90V<~C_vD_y=_VJc3*1PG+33jSuk?n%y zr)Aq~b`Gxyh!3ymn5>=PX{v%6R}4&+|K)&DM@JGnME@s!M(l)o-WtK7$NRF2@9HW# ztI%ZjE{=)?=smAp92;!~*FR{2*smU=vX~(2C5>mCo^>j;eA=Zm4wY^366dh1T8vpE#y-k<-N;!Ihl~0FTI*`Oo}VNbWDX+gxQ-)~tTbxpu5W@0TeQTs`G( z>T?a>VwcSbyKFE3DC5BZ{=9{?eINK#s(Gxbw9^I z$lm^kZK<+QSyY$x@IRcR-yX5V1N7!Xue~h$VG}XBqid<$%Yny;*qwMB)Lf>j{x!bp zN#m-W-E^E!o@`|Wm$ia3mHjujNUD-n{||eAv(Z5KSO1?=n}Q-veM<>xU)vuvEF2Jj z!qp~aoFV31d&F&N9iVQjp1Ut7fM-5cmF^*`p18fwD3@XcF6kK^_DKCIWL?FN&;Rg`w1xf|LL(=l< z&WE$n76N=diKw2>bYU%kCC7>Y$_@jVNGM}a9x!1D3n2>thB7MxL^5nJz*R#E4Lt=1 zTEOixEaIvSo@2m~kD36B7;kG71m--GXeiO<9!fLy*C+-}V~YjBd&=e(n0x8s6IftQ zTmI~c1D7;=Bi8UvoA$#t$6}wKUsyYlJ{Gi((XPN109i>@1kEga$HQ67vQ^d?u-Z_Tzt}Nt)HTN?CH~0|&rH$Jn2tpr z^8}7b65avchvO(5iN2{GzWMOfB~1nPb11n5fbRkbN@^JJXiR+`TsvNl8Q%(ssjU>4 zpGNoo8xofK&@em*ZFYA9pmjMz^i)G$JAlUs2zKa;2|_l^MRT4}SL;w7526puL_)MA z21l~j5d9Xgs25P~O2GV1jiHJ1adcf4aAD98SonJcnJA7*wSl1^p-&1*#gZd^*u*&# zGQ5ms3i1(YQz6Kl17M*>5Un_d>e&%YFI4cLEdbOQuo9$2V8NC^tq>@m0QgQuNG1Z} z7<+mv=$=$v;fmh^-bdiNZTV(EP!Y$(kph53P-Ea18#)$78@d4F(*nuY_!vO9S3+aJ ztr|f(b|vuo5PhR*Vf7dZ?%buPP~5q(?&t%jOTZ>AS*Z_^70L(L^xZOwePlyS;&6M+ zbKFXhsXi5>sHT9XXmB+kXQ6R~p$=t37l8sue$EM^3qZO8uZ;eQw&b?;t zkO9Y??oMIi%2tN@JoX6ER z{0?38vNfNcDt8zP@Dl{<(-FN5#qboO{}f`tcYghAlHf*o7Kn(UPLOXlZ3T$fFQ*+y zg@^G3G_xPt!Tfq6L=^@ikGu?GZ@rwCZE>W%D}0FAzzBqcA%UsXSegVR zQmY`7t^7XN9<6=x%<@3VfD%AGsP2U5`FQ4lq?u7QK>yrHX!aT9o(aHO92xDP2NSug|Eo$&G1zTzltQhoZ-XPQ`5Ap$) zvQ6fTeg>Q*YfBx?wScP-zKu6}Ji;|5x%?9`h#Ew7D503SMSd!osbl zjzarG=`>|_6PY}VLDPPyf0IzI=?x>!k*GNoSmE5NWn6u|5pe~Iy11_jJ05g=JLVl4QMnq~{7{Sy8PlJJh)FDp^1 zrSFOSFIq3$=QoR*0|Y?XzT9^Bt%%G*`@oKE%cwxsYlB@_{7YH`8XNuUJx@ZJ@q!p4 zkbmof4h5|{z>D7gEgOhP>Gs&_=qBay|$&IW+15614EWk04xZgiDfH4#!FCPzIOBT zRlFK7^WD#(E%Rsdi3*`T-Ld@UajV_%LK#ouy1E`3+hWVo9!zc}jD5xd47k0PuLNdG zBzHsg(0Tq2C?^DdQ^lc(AKWt$CseZnMf4UR^g4!*#9%I4Luy9k__CN)RB&~IN|QhV<1mK}@lJZjigQQ_~A z_iD~0p@w*Fu9%H;Tz^kB<7B_v6m8DlO|0`4{f-ymmGjZ4h5Y)W(UD35MGChyZK>I9 z!D8TU1hq$nJKN-v(YxB6lKsIW5}hGEL%|M1G_RXJgu7*9YfQ<# zV^?B3;eEy2xvmNN-=i1ZAL;11;&1DzT6lp$y?aLj0zBM;%?@Utc`C)M5NAkRe=a#i zx|lb|`rb$Sw?RUB4htFyphpUF(Ko@mLu^5SRhm5mIOMif0C9VB|T z9)ABNg(cbM%WgWZ-14bh?^L8)x$}a9Z>(DX)V8kkl?B$G`d94}0y8uJdbang7usr& zVy_ybjxGhEFRsO7Tk`fc|Gi+~n>9u{H8*nQX3C*vn^!V7hZgGM5>C&|Z(P*BT;&+} z?E7>NpOqgk3NX*C=qXwo{)MzAsPsu%lI5Q~-BR|;vi!v}yJD)SQ9ruAMednsZJ65% zvC*S|R4cz}dh4^5!KTfOWfQ%g_=m5&D?v;2YDU@h z0heVgTB!q1xlfmMXE4okhw ztuJE8Yl;EgtdPX5SD0>IyETuLP;xoMOCf+}`OWtQBvK1-jeKs;>bO~HGm`f7`+;b| zAJ#et2cx{cS~dFZcqXpgyJIwAt9}(a!ch8{A3XKmE}q&Z&2pi)Hs8=1*{Cjde@bPo zS|I7IXIxFOQg7?I!PX}cmjmx()U;EY2;hWn*4#wcdjH;bIo4Uh7r z8k`5{2A?ykLLTkb_=mBs0ptk4zHqfK{J@tm~rW|>!@I>amnKcf-Gl(Zgke14HauPPpe4vet<0>6S z@)-V+3l~Ge_lOA^nAO?S3nmPoj+P7>-XTwjD^Fe5!f*nrb}70{tV!(}9(c^2et~J( zD0`pX%Fk+SdG8D}Gd3dILp7xP2h zhJ~|cKOZW8yR#uTDuVlLg>vdlFHf(*;b&9Tz}Eyrr7-s+Vk7geYYnyjT&#*dmN8vN z7vZQg2%TT*R*cc_xSvNeeIr@%P1D8fV!KRV5wVfM(NxYw-+AkgRtpa(m0Q{zJMG=H z8l9fcC@X#F?O;j7%-Gt}M4yN-^DYy-#tO$>z2BS;-_@o3G?%ls7`@GTScdUn8by42 zSTZQ5QK}v`Aw4>w=4Y*8Emj$|nI%QRF{vSP*8=S9w^v5nirX|U@h8_k-_8ovtVC<_ zHaKl3$zAIYvlM0>0lE3WYA|28T%FBZ3}f{Q^JGTFW!v8+4TVkh)F_q&5c0_PN~9WBCVo-ZP`P3!)LrZh`%yA?}uh7Zj1 zJFUUVyX~gA3K%t+u}zrUo!E4;y#=5afbor3=CBOdlw*ojD-l5A@aVT~t3WP{kvXXG z!KAOp{NG=w@xen;Y9*3T6e@5OD=kb3f|=WhwC)5pS5eLI71}{I!J-$Wh0zO#HUes} zZC5Ds?fDU3E|21K z^<28J%W|PaC>KinpK^aIWF|o{yMT=~^1}bStK=RuVm%30!m&i%yA5JyV2e3WSH$G# zcJ0HeRSFXP$RX z+0Y_6ZG&0^v(Lq2jH(QhuH21pj%{Zxa$C8pvU0QtS?Wpi38TSv<*NkCM!k6D3$w1_ zKLV4Ah6GD0DEXBoVJp^_@xoU;y8g;}C~J0UqR7fU#Dx@fU&?TH-CR6qDgBEVkkfW$ z=}P$eKTBAF!7brcCB0da*@l}>uNtaq_nzwvA9~`%Jw4a4IQ4zLH{^XtYRGKzM19PDv(|Q+S#p~`P8NsE;sgoZs zMcsOQAPoEgcivfY|CY$IfajUj41FPv)p}SLt+-ksN(Bl^H^l`huAvEZ*X9V@Q*bnoWFpz!Fl9;dKR$P}*uf32zMgpXkRUMoi&HQ?&>r1?$y-rg6b zEy;YeR<70`&%9|cw##oa_?k3WkK*&Y?lAo7dpibhL7;_6a}P_P6-Jry!%8Q|$L7qp z%o~#>>&;32vIJy={fK{2J`~d}D2t6lk=WN4-EB-(;q~kE1+T_!W zY#j1#e$*pq19&$Z3b*qv83eM*3erxpRdv~G>63mJ(fZf-QeZx6=>_T|{ocMP2k7Io zOH?gvx)xou+_tTnWnl*{Ae;5H;E;qk7XZ>_lA)6C;kk23jHFDk5_8$Mwm%_(s)Xo%kBytFvJJdL1K|(ETz~ZmG4w(iB+k#FoP!!F0N=lxW{sQW;KXIpO^-dd^0L zXv9kb+XRFbPwJBLWu1m+!!^BeMC_wSDMe#)wUqoPBfHkoT&DVy_o4VGEdp~EP?{=AF~O4pI>3rfb=J)?f#NZm&X`ubK zU3@}|{$07tGjBGsJB>tVQ0L4HMDgTO&Xe^^rFF@BC|d7dP?X6aQK+$&Qj~EhpNj7N4L_m zbO(Hm@I>G@sam&=Cb}LzC{AL4@q%TU?z?V@0`0<;HvAp#Qc>4o7f>*z#na>+`Aa{x ztyoB1$rt=Fpq}+NU#l;!+t-+Xxz5!`7WnwB`(>;`#x9_r+ODk0PIZN(N39MJ5aM`W z;1`GQx_!R;_{ZQAYb~^jsPJmOhyCIuT0UIgCFvePYeRv-&!aMYtwk;CXd8Z8t-YQv zQ?@&uA<2&fV@K25(+M);!G`j`6;5S7`|tk8ubnPfK6UFyB14*g$_>oBNb;qtH;bQe s1%=-l8d#V3uDQKh|BEhU^(9hWpYOo}x+nZk0svT;SpD(bm~!?%0H76-Bme*a literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/motorbike.png b/app/www/img/carimg/motorbike.png new file mode 100644 index 0000000000000000000000000000000000000000..34cac6c2c5f55d5d807c0effd1d1875629fce7b8 GIT binary patch literal 10760 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 S97J*<0D3w`CrY&(Z~QL>TJN9$ literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/sailboat.png b/app/www/img/carimg/sailboat.png new file mode 100644 index 0000000000000000000000000000000000000000..b3f77c08b8483186cbbe65b099635382bbefc930 GIT binary patch literal 9980 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 beZAz~%D>e-@szz5o9J=B4L| literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/sailing-ship.png b/app/www/img/carimg/sailing-ship.png new file mode 100644 index 0000000000000000000000000000000000000000..9fbccc0d2c7bce4b18198320dedb730e47eb4c99 GIT binary patch literal 9919 zcmb_=2UwHYw)Re_QjDlb6R?~S6)6JJL$HAg3NtEoXd)oJ1f&E=PyroAP*FfyP?|~$ z9YIJ06{UsVAs|W(5PEW9h^@<~$K@*pn!h?K^ULs<)Qh8L z+=?~72Z`|AUBF;-7n1C&7A$6#D_0WHc&w%*Qlg+?1x;RkX9W$+uK1hAAQ9vIiWI`4 zlg*}wP295z`Az36)5GK9`(OdKw5T1JQ2&Es)$bUyBDRg%0DPBZ+SYup;bVrhQZzby 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 g+oL^?@C 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 literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/spaceship.png b/app/www/img/carimg/spaceship.png new file mode 100644 index 0000000000000000000000000000000000000000..f123dde41b1b071ad3f103aab9ee3b9629c0dff7 GIT binary patch literal 6114 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 VZ^N%dx9xBpY;|&Td|)4N=I`a`%4`4t literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/sport-car.png b/app/www/img/carimg/sport-car.png new file mode 100644 index 0000000000000000000000000000000000000000..6ae0297e1eef5d2efec37484d87c19d9ee5e240b GIT binary patch literal 8863 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 literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/station-wagon.png b/app/www/img/carimg/station-wagon.png new file mode 100644 index 0000000000000000000000000000000000000000..d08b2e22ec0a0ace3705db345f16d41276c0bff7 GIT binary patch literal 10002 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 literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/train-1.png b/app/www/img/carimg/train-1.png new file mode 100644 index 0000000000000000000000000000000000000000..0cc8e8dfccbf0573c5126a8dfa5de7a5cc0ef941 GIT binary patch literal 8723 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Ξ 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 literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/train.png b/app/www/img/carimg/train.png new file mode 100644 index 0000000000000000000000000000000000000000..7cfa2834ff2579379f3e0cf8947929a2ed2a56c9 GIT binary patch literal 5561 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 literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/tram.png b/app/www/img/carimg/tram.png new file mode 100644 index 0000000000000000000000000000000000000000..cc344f2e50fb005b7d9c1aa0f5205cc8e920c8d8 GIT binary patch literal 7219 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 YpopRt=}pB=0R40?8eJ$lXM5+r0Hag#;{X5v literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/tricycle.png b/app/www/img/carimg/tricycle.png new file mode 100644 index 0000000000000000000000000000000000000000..9e078b53aaf2bb9021b999be912fe2ffdd78babb GIT binary patch literal 9723 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 rWTJ0XTmg)q}DRC3sn# zva1wV0f6kGg9nZR;IJ+Zq$IG0AM#FsHTb|^57|p$pKz(uH?aTG{s&J4V)Pv=U*y*v z$syR_mWyVN7w!Bg7lVHfI0M1K!P?&EeF9JY?tezxFTgW-+*lESV0GxgKKqcav5zu$ zTxV#6Ns(!m>mg0)pOCvdfA4h1uaf!+xzw-!ywM;g@=&t=P07NK)~@AxLD@yw{Ss^= zYaz2NZgXy*oshUON>4TsWNNqTy5_7fwGd&ZRNXS!SnuV=2g1b?A!?XY7} z?j3zhnXzxK9^XOjvbd$PhdFdSQ?S8A&s5js?noo+e7$J?K*8R?3R1@|R>7N6-Hrg~ zRgeA}Fq12P%=4ZgQa@~Hp1#$y$3t%>2Dr)pVC#1*P7&yLMy6Rkr@3NUt0eq1sJ(I- z3g$K?C-CS)xn;dT*96hLS3&HclXos?l|AKMi#7L)R`HIL0;?aE{W!$Ap%=S57+SbV zQS_#YZ_63Se*}GxN#fg?;iWp~z*OHTNU`PDJ7;>DPm~zqQ6o8#)RBj!ae z5iG5Q>Eke9My$x^J&3uG=q1jpO z*2yTaRj1$aiE9KMzulWEKzX_v21K$xCvVVy9y*T)hrehiCJi3aY5i8lko$KYcyrTOYQpnh)Lji4$! zp`?EP72j;3#!4NiLrWP7s&KK5_#O|`B5-d;mN zx}3%_Eo8v=7gODeo$M4pOR#TWFe@|%-XmX@;G6{d=Bp-k7mjRU_zcfHE(2O$KCDMi zYQe#H;I-k%nYV3@6gCla&qyj%VEg8wOD4vaZ?t@c62ja#bQk#N5$3fNOB}ac9MsoCg)?}Se7iaP^TL@- zEyQ-OQj|&z_&b$#nL}x+pR=z7(s$AD4?Ht+g{dFcgNX~=Q9-!X;;6Y&I^$%Vn?v_{ zC@p=#8<2v#s_029gm& zVJQgwd}ke+rVS-`qRfGR5g|L_$uM2i9jT3lPjnbR}-} zU}qd%vkBpi;m9&9N6at^@ZbROFa6UnO>c-Jn4)S=9OsANL3$lIRslj{0uKQ7C2Up3 zG3#XDHs%)}6Wq!=*Te17eBO){)P)=?$iftLN=u7sf~(T~K@rCt5l7_%E?`0s;ovpk z&HU32L=I5_6Zv-Vh308-NOo_}VqM1!SgWK#tpxWCoB(Sna+3l$)f;XD6E}FW8MxE` zQr4etyB$|Se$ih%A&%T&sDNkA$$&fpR5oxW-ijmGFuG9+?IC_w8eem|Vau!-+HRQ1 zKyYMB!zDR)2`TWM9&F4^h;=g(jLr`>SF>Wi&`pzk29p{y52cO7sW=I>SvBCe(1gpffE!jLrefEw*R3_(yb( zN^K_l_-a!w1NWDNFHY`4v6A*H9`aJ4)sG{C(_WdbA0S*rK-=HqECBrRWblf7ol|1W zPVxLGR?-t%b7UOc4{739Ce3dn@nDa^}aL8 zW^BQc_Zuw9adFmSQb4p{##LRrv=e)TQFgO&y*5^Mu8uBE^=T^UE7h^OP9$N+No zRH6(bj%|nS=Z|MzL)-&2wB8aG?uW*j?N0+KTeV^vi61nCyA|a14#1@YK zoNaX`41@&_``wX-ih)TISxF3Sg?u>}Ai_p~v!00-VtaA6XZu5NTgh5x6XQe??6H8a zUI7~UZ;3t)FYDevQ3Kq&=FvE$L4`+b}T{V-E8*7pWZ}h2k>y5m7j_s?~d^&|C(yzcRO)5BFyc(2c3=%%7zQ&n9D}ohJ=lJ zX7K3dlmcK$PU*9ELU2-m6p>_A5a6o3cPhFe_(yc$9U&^fS4`^Lg9%n=dQQ#$z0L;k zk1BWr%Fmf$cDl}Z^j7)K4)>f_JWR;I7~H*y;j1<0)`7-baTG4T$lf#lYt?oMv}Ne* zeM6BPcXNY_KX7+K4_ARja+;n#iZgeur` z7FhS=L$B(xg<|>+lGLTA9_>=Fsee%foZwhL&WfDV1{hB+X{+CVfIjR350=;M8T_@% z9FN_pjgptN)u&Zdud2(+z265(8q}qF_u*%(2Mj|T*~}#=_ub4QW@zq}6+;VcbptU? z?4TW_Bx3jZ_A`z*S}P8ipL6q1_TG>|l#zPD=Y8vNK<+myoY16e5-i{`rq=v=V&RU` zt=H`oC-GzqDS4bY#O*hZ3c`HL7D`5nkgnE9iB7_ z`Rh&SPnP(BcxD0yQ7gE3rli_@5UAs^yc=~tTs(FtJ%rUVvG?(@qH$&o{&|fhR-e?LwnqM z27K3A{CHUT)rENiv&I5>(}arWe97e5oSLu8tEgmD1kTiYl%*TMmpwiA+#-bf5f9r~ z5rqxusS_eG?#LGISSzrPb3WM3jMPMvKCMH86Xx>;NlSyokfGbiOlH~dPu(x$>ywqmPzw;VEd|^1)|J-}1~Rt3adyRSZqG|NVHbJQwxE9?GHrF3w{9ku zT{dbDfd9f#s#Fvb*0~at$HC2Ln7qSS$NbI1o^7~gU+gN?7W`$JxRrKZ^^q-l)Jrrj*d}d)_^p^?ULClj<4n$U}U%I4Q zj7K$=W(g~tB~Z+?VsIsDfPnxxm~suc`zjHrdg;@;2hkt^hAqaK-j<1Tn`iMDG;F2R z_%Eo5omZzMfNhHzOMYRDfF}JQ?r6`^MwmZg;_;HoTqLF7m_wy`3%pScNmw!TIKq6IjVRLaReJz<-&SA1 z_t_t@Zd`V8Zg^>7+~|F}9w*Vqf3-mVA_!@R($%9ZtF^p zja>~ZDNs)I_Fc6S;4aFz!(&}bjnHjO%pv=v$P-r9S-KU96S>1M>}j zIvtPLG1DSo==!b(EK6{&#Z3IKA1pRSug=mpcE60%A$inB>+^n{9+E_>gO;OSwI%LN z`|YrL)?lIHq!n6?rPs0V4Dq9ejF#XuEKkG6J=))e&y%JyU{WAEOpajn?o$D;2a8cB zL>a2^iQZ)j+=F$Tc?AiymlIUA8;6=3+H&UmCD9JQgK~oja$VNI-9gw%S(1*0KSXy% zc6YA=#ZP4(W!Z5OXv*4{!GzpE>QU4)7?>nNb9G43{eM12?fzb(rPNs4}rA@2R`_HLq zl;IXeTMZ|s( zCtqw%cTIS^SW9>~L#Lf`n(Y_G3n(#8ZR9#Lzndx>Dz;Ot`Dwg;)Nda&DIZ4j8uV;n zN6G@vZAL3}>dzq0<;mo$nkvGE2ZIip?BHOh#VRfDYLj#;g%1ma)=hgVO)AFiNXgn2 zV_hYKmz`UyK8d}M_wUMxJV3mew!Y$L>Jfet&#*ZxLvpw#u#j<~xLCW%7(ru{!Y}zb z?F#bj;(HP->2vI*aBAB}cCvY}sQx-KxRYwmkKt`=w#>*r;P=-FJSw4NG=Icsu1BfG zCSu&myFXn-Q8f0Fl(3$XIWWGiSu?})z{CxeD?q+o!zvG&vwF}GF@C%yNz$))AF6P( zXRXPP)N4LB@t{Zv5fzjS=Mt_xEDU;yANcc+$;E85UcC>+VSkuy`?5`z*Wp zW4G^WuOc4OER&&z&8&|SQ8Zi^YUoL|D)jnVRQ?u?L?!cacyQXTJ=sZ}F5d-SpNLR2 zQC@EY%*JhfRpZYM%@k>Z$lVepO_F9PKYEyb4vi?ZO$W;%B+|B(>SG_jUWA&w$b*9xNFAbnPd5jqZ*5wLfmZMGSeNhv zsYoL$w>_8jZse_1(=w-_we`1ep53RUlRT}{ZqR`UXjsF+I!maIxrZhz0j`e zmfO}8etLTrVPd9Jm*nBAoYQH>FOmzJQo}*^xyGw*1s|JUxl~XtQXtX<|zN%DO)RBUe>xm_TzXU>wqH3|XPafW)b=;<}1p_M| zl@}G4tWMXR^o_Ny?Hg*h{}}OgXx4RQO0_)rf!^6mHmF#n9raC+&BcRxS&nu=>le?B zmT$$XHD8TRuIA~3)=A4;*QH1xt36i(-+^+G6qGpmPDnp#^0Y84x*k49Kv0L={<)hp zC3JVewqBtHFf+e~*r2YGN0TnufVEv-s%zz9EWoY`HFU37OGat`$|jsBr=AT}-r!Vx z>H8j`N9)3HqrupM{dtMP?+ZlfVK}bD_b$0fv5p%n6x?H2f5XB!35UhK zIXu&Qg$EdXbbk)-uZT+I%YK(b?QED0WLt(_cHHp&jBGA?HrNJz{yHiXUg~Tf&+l!_ z_|ANq({!J2=_0Mm8*0o<{m@bBY^YkNmRi=pT0hnmFAhe)N~C4bH&$yl^@ZQ5T(P}oc+7Smg%IJQe_|GcIxA*^|9Y!^g9A~{YZ`AV?{Jw842gEK&LGbF_z%)z-=KX zD~DZENBX?>ecSP!hX@f3kJAWilxUrIOD6N0mjX90>C@OF$*v#CNU z@tx%brTflfM4gDXmJ&*h?HpZZ3(r#3M}85!vK$vH3_fmJq>fJiph?-!kwTb%m8Fte z9#0Dcmwit^AqrDv%IWs679zSv{R(AxW$!yqr4KpkDtVY=1+2DNO;ek}_@RsR?$r*H zx4gUrrrapn$7rnf_Waly$Sr643dF&wKw*(goDI_Z6?m*q+ag6a&wVAZDVG_W?imjP zucq=mwwcKsy^K)uw@u%lb!lS)`gHx}>fOfq?NPM8a+CKRjTmD?Dm~{vr9V+0g^GuVjt!I zPxbuc{7au_``w$S`&}bj3A8iEG1W_^zfX@jLYUge)hHS~H8yv6bh>C_I)x#vtJL){ z2D+CVRE^hC|G3Eb!dIXvDsoxv$G+6m4wklk(bKJahGnCs`yl9TM(v9 z35yYYv@GhxTD@(QtH8yT&#EK}tLQ_6*)8L=vBh_DFAn(4e@|nQx+F8}EDB@O z2o)p>Ihmz;V0OMWvenI4Cw19?7&F2aTvQ#I4j#1*?jfbBgnjiNCA53bQHMD% zALCKAJV|iMSg%*8fw-5?0NK*sWANCI@L}q&ps=Y1Qy(09z56)F7^(UVU}?5@9Q@OR z5OVb~O8YT&_;Ah@xc>2pLC03XvjL-XzQDDOj5?tP#=Tkur)Qe%58gCvC|JJO8Vq8H zjs(!R&!V`r;i_g>Do@S9ixGKiiyHjUgi#0o#CGvX1+GqqydFkPZMPh;B})s zs?$OJ7)`NnZ`@unoGpFvR-%Jp{Aw^%uerUx=dMp}7s7n#alImo;f+07|3FENL_$df zR$i5pFV4Fw$bC65b$!t*Vj+4Lp4=Gzknwwb8lOwXI%A!L7JHfM6DbU0voA8nyHSgpfKH|ncEVZ`t6>^KrC#ya0P6XQZk^x0oo|$I-V7X@jowN_- zfs1^;U98%!xvs_en5&`PCJ*kNHLo$vf0pV&;1sJ;4#s(&&J_FKucZI~kDmX@v+v)B n;{P^UZ^bSOmJ4u?h!p;2JDuQCZ(^kffJ0{12MT_5zw*BT?OiJE literal 0 HcmV?d00001 diff --git a/app/www/img/carimg/volskwagen-van.png b/app/www/img/carimg/volskwagen-van.png new file mode 100644 index 0000000000000000000000000000000000000000..d2755c217c485f3d8b948e05119ee1d889f27850 GIT binary patch literal 12794 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 T;FQWgM=Uhdbg#Wru?YQdP{W3? literal 0 HcmV?d00001 diff --git a/app/www/img/ionic.png b/app/www/img/ionic.png new file mode 100644 index 0000000000000000000000000000000000000000..21c7f375997e6443b05e5fd50815271da1e2c3ec GIT binary patch literal 4757 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 V)A2w7aX)_)nHgO`RT#L({STJyPAvcc literal 0 HcmV?d00001 diff --git a/app/www/index.html b/app/www/index.html new file mode 100644 index 0000000..b8a8028 --- /dev/null +++ b/app/www/index.html @@ -0,0 +1,29 @@ + + + + + + collectivecar + + + + + + + + + + + + + + + + + + + + + diff --git a/app/www/js/app.js b/app/www/js/app.js new file mode 100644 index 0000000..a313e16 --- /dev/null +++ b/app/www/js/app.js @@ -0,0 +1,73 @@ +// Ionic Starter App + +// angular.module is a global place for creating, registering and retrieving Angular modules +// 'starter' is the name of this angular module example (also set in a attribute in index.html) +// the 2nd parameter is an array of 'requires' +// 'starter.controllers' is found in controllers.js +angular.module('starter', ['ionic', 'starter.controllers']) + +.run(function($ionicPlatform) { + $ionicPlatform.ready(function() { + // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard + // for form inputs) + if (window.cordova && window.cordova.plugins.Keyboard) { + cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true); + cordova.plugins.Keyboard.disableScroll(true); + + } + if (window.StatusBar) { + // org.apache.cordova.statusbar required + StatusBar.styleDefault(); + } + }); +}) + +.config(function($stateProvider, $urlRouterProvider) { + $stateProvider + + .state('app', { + url: '/app', + abstract: true, + templateUrl: 'templates/menu.html', + controller: 'AppCtrl' + }) + + .state('app.search', { + url: '/search', + views: { + 'menuContent': { + templateUrl: 'templates/search.html' + } + } + }) + + .state('app.browse', { + url: '/browse', + views: { + 'menuContent': { + templateUrl: 'templates/browse.html' + } + } + }) + .state('app.travels', { + url: '/travels', + views: { + 'menuContent': { + templateUrl: 'templates/travels.html', + controller: 'TravelsCtrl' + } + } + }) + + .state('app.single', { + url: '/travels/:travelId', + views: { + 'menuContent': { + templateUrl: 'templates/travel.html', + controller: 'TravelCtrl' + } + } + }); + // if none of the above states are matched, use this as the fallback + $urlRouterProvider.otherwise('/app/travels'); +}); diff --git a/app/www/js/controllers.js b/app/www/js/controllers.js new file mode 100644 index 0000000..d9a02ee --- /dev/null +++ b/app/www/js/controllers.js @@ -0,0 +1,59 @@ +angular.module('starter.controllers', []) + +.controller('AppCtrl', function($scope, $ionicModal, $timeout) { + + // With the new view caching in Ionic, Controllers are only called + // when they are recreated or on app start, instead of every page change. + // To listen for when this page is active (for example, to refresh data), + // listen for the $ionicView.enter event: + //$scope.$on('$ionicView.enter', function(e) { + //}); + + // Form data for the login modal + $scope.loginData = {}; + + // Create the login modal that we will use later + $ionicModal.fromTemplateUrl('templates/login.html', { + scope: $scope + }).then(function(modal) { + $scope.modal = modal; + }); + + // Triggered in the login modal to close it + $scope.closeLogin = function() { + $scope.modal.hide(); + }; + + // Open the login modal + $scope.login = function() { + $scope.modal.show(); + }; + + // Perform the login action when the user submits the login form + $scope.doLogin = function() { + console.log('Doing login', $scope.loginData); + + // Simulate a login delay. Remove this and replace with your login + // code if using a login system + $timeout(function() { + $scope.closeLogin(); + }, 1000); + }; +}) + + +.controller('TravelsCtrl', function($scope) { + $scope.travels = [ + { id: 1, title: 'Travel1', description: "description for travel 1", owner: "user1", icon: "car" }, + { id: 2, title: 'Travel2', description: "description for travel 2", owner: "user2", icon: "station-wagon" }, + { id: 3, title: 'Travel3', description: "description for travel 3", owner: "user3", icon: "van" }, + { id: 4, title: 'Travel4', description: "description for travel 4", owner: "user1", icon: "station-wagon" }, + { id: 5, title: 'Travel5', description: "description for travel 5", owner: "user2", icon: "minivan" }, + { id: 6, title: 'Travel6', description: "description for travel 6", owner: "user3", icon: "lorry" }, + { id: 7, title: 'Travel7', description: "description for travel 7", owner: "user1", icon: "sport-car" }, + { id: 8, title: 'Travel8', description: "description for travel 8", owner: "user2", icon: "jeep" } + ]; +}) +.controller('TravelCtrl', function($scope, $stateParams) { + //$scope.travel=travels.get($stateParams.travelId); +}); diff --git a/app/www/lib/ionic/css/ionic.css b/app/www/lib/ionic/css/ionic.css new file mode 100644 index 0000000..9dd5268 --- /dev/null +++ b/app/www/lib/ionic/css/ionic.css @@ -0,0 +1,9813 @@ +@charset "UTF-8"; +/*! + * Copyright 2015 Drifty Co. + * http://drifty.com/ + * + * Ionic, v1.3.1 + * A powerful HTML5 mobile app framework. + * http://ionicframework.com/ + * + * By @maxlynch, @benjsperry, @adamdbradley <3 + * + * Licensed under the MIT license. Please see LICENSE for more information. + * + */ +/*! + Ionicons, v2.0.1 + Created by Ben Sperry for the Ionic Framework, http://ionicons.com/ + https://twitter.com/benjsperry https://twitter.com/ionicframework + MIT License: https://github.com/driftyco/ionicons + + Android-style icons originally built by Google’s + Material Design Icons: https://github.com/google/material-design-icons + used under CC BY http://creativecommons.org/licenses/by/4.0/ + Modified icons to fit ionicon’s grid from original. +*/ +@font-face { + font-family: "Ionicons"; + src: url("../fonts/ionicons.eot?v=2.0.1"); + src: url("../fonts/ionicons.eot?v=2.0.1#iefix") format("embedded-opentype"), url("../fonts/ionicons.ttf?v=2.0.1") format("truetype"), url("../fonts/ionicons.woff?v=2.0.1") format("woff"), url("../fonts/ionicons.woff") format("woff"), url("../fonts/ionicons.svg?v=2.0.1#Ionicons") format("svg"); + font-weight: normal; + font-style: normal; } + +.ion, .ionicons, +.ion-alert:before, +.ion-alert-circled:before, +.ion-android-add:before, +.ion-android-add-circle:before, +.ion-android-alarm-clock:before, +.ion-android-alert:before, +.ion-android-apps:before, +.ion-android-archive:before, +.ion-android-arrow-back:before, +.ion-android-arrow-down:before, +.ion-android-arrow-dropdown:before, +.ion-android-arrow-dropdown-circle:before, +.ion-android-arrow-dropleft:before, +.ion-android-arrow-dropleft-circle:before, +.ion-android-arrow-dropright:before, +.ion-android-arrow-dropright-circle:before, +.ion-android-arrow-dropup:before, +.ion-android-arrow-dropup-circle:before, +.ion-android-arrow-forward:before, +.ion-android-arrow-up:before, +.ion-android-attach:before, +.ion-android-bar:before, +.ion-android-bicycle:before, +.ion-android-boat:before, +.ion-android-bookmark:before, +.ion-android-bulb:before, +.ion-android-bus:before, +.ion-android-calendar:before, +.ion-android-call:before, +.ion-android-camera:before, +.ion-android-cancel:before, +.ion-android-car:before, +.ion-android-cart:before, +.ion-android-chat:before, +.ion-android-checkbox:before, +.ion-android-checkbox-blank:before, +.ion-android-checkbox-outline:before, +.ion-android-checkbox-outline-blank:before, +.ion-android-checkmark-circle:before, +.ion-android-clipboard:before, +.ion-android-close:before, +.ion-android-cloud:before, +.ion-android-cloud-circle:before, +.ion-android-cloud-done:before, +.ion-android-cloud-outline:before, +.ion-android-color-palette:before, +.ion-android-compass:before, +.ion-android-contact:before, +.ion-android-contacts:before, +.ion-android-contract:before, +.ion-android-create:before, +.ion-android-delete:before, +.ion-android-desktop:before, +.ion-android-document:before, +.ion-android-done:before, +.ion-android-done-all:before, +.ion-android-download:before, +.ion-android-drafts:before, +.ion-android-exit:before, +.ion-android-expand:before, +.ion-android-favorite:before, +.ion-android-favorite-outline:before, +.ion-android-film:before, +.ion-android-folder:before, +.ion-android-folder-open:before, +.ion-android-funnel:before, +.ion-android-globe:before, +.ion-android-hand:before, +.ion-android-hangout:before, +.ion-android-happy:before, +.ion-android-home:before, +.ion-android-image:before, +.ion-android-laptop:before, +.ion-android-list:before, +.ion-android-locate:before, +.ion-android-lock:before, +.ion-android-mail:before, +.ion-android-map:before, +.ion-android-menu:before, +.ion-android-microphone:before, +.ion-android-microphone-off:before, +.ion-android-more-horizontal:before, +.ion-android-more-vertical:before, +.ion-android-navigate:before, +.ion-android-notifications:before, +.ion-android-notifications-none:before, +.ion-android-notifications-off:before, +.ion-android-open:before, +.ion-android-options:before, +.ion-android-people:before, +.ion-android-person:before, +.ion-android-person-add:before, +.ion-android-phone-landscape:before, +.ion-android-phone-portrait:before, +.ion-android-pin:before, +.ion-android-plane:before, +.ion-android-playstore:before, +.ion-android-print:before, +.ion-android-radio-button-off:before, +.ion-android-radio-button-on:before, +.ion-android-refresh:before, +.ion-android-remove:before, +.ion-android-remove-circle:before, +.ion-android-restaurant:before, +.ion-android-sad:before, +.ion-android-search:before, +.ion-android-send:before, +.ion-android-settings:before, +.ion-android-share:before, +.ion-android-share-alt:before, +.ion-android-star:before, +.ion-android-star-half:before, +.ion-android-star-outline:before, +.ion-android-stopwatch:before, +.ion-android-subway:before, +.ion-android-sunny:before, +.ion-android-sync:before, +.ion-android-textsms:before, +.ion-android-time:before, +.ion-android-train:before, +.ion-android-unlock:before, +.ion-android-upload:before, +.ion-android-volume-down:before, +.ion-android-volume-mute:before, +.ion-android-volume-off:before, +.ion-android-volume-up:before, +.ion-android-walk:before, +.ion-android-warning:before, +.ion-android-watch:before, +.ion-android-wifi:before, +.ion-aperture:before, +.ion-archive:before, +.ion-arrow-down-a:before, +.ion-arrow-down-b:before, +.ion-arrow-down-c:before, +.ion-arrow-expand:before, +.ion-arrow-graph-down-left:before, +.ion-arrow-graph-down-right:before, +.ion-arrow-graph-up-left:before, +.ion-arrow-graph-up-right:before, +.ion-arrow-left-a:before, +.ion-arrow-left-b:before, +.ion-arrow-left-c:before, +.ion-arrow-move:before, +.ion-arrow-resize:before, +.ion-arrow-return-left:before, +.ion-arrow-return-right:before, +.ion-arrow-right-a:before, +.ion-arrow-right-b:before, +.ion-arrow-right-c:before, +.ion-arrow-shrink:before, +.ion-arrow-swap:before, +.ion-arrow-up-a:before, +.ion-arrow-up-b:before, +.ion-arrow-up-c:before, +.ion-asterisk:before, +.ion-at:before, +.ion-backspace:before, +.ion-backspace-outline:before, +.ion-bag:before, +.ion-battery-charging:before, +.ion-battery-empty:before, +.ion-battery-full:before, +.ion-battery-half:before, +.ion-battery-low:before, +.ion-beaker:before, +.ion-beer:before, +.ion-bluetooth:before, +.ion-bonfire:before, +.ion-bookmark:before, +.ion-bowtie:before, +.ion-briefcase:before, +.ion-bug:before, +.ion-calculator:before, +.ion-calendar:before, +.ion-camera:before, +.ion-card:before, +.ion-cash:before, +.ion-chatbox:before, +.ion-chatbox-working:before, +.ion-chatboxes:before, +.ion-chatbubble:before, +.ion-chatbubble-working:before, +.ion-chatbubbles:before, +.ion-checkmark:before, +.ion-checkmark-circled:before, +.ion-checkmark-round:before, +.ion-chevron-down:before, +.ion-chevron-left:before, +.ion-chevron-right:before, +.ion-chevron-up:before, +.ion-clipboard:before, +.ion-clock:before, +.ion-close:before, +.ion-close-circled:before, +.ion-close-round:before, +.ion-closed-captioning:before, +.ion-cloud:before, +.ion-code:before, +.ion-code-download:before, +.ion-code-working:before, +.ion-coffee:before, +.ion-compass:before, +.ion-compose:before, +.ion-connection-bars:before, +.ion-contrast:before, +.ion-crop:before, +.ion-cube:before, +.ion-disc:before, +.ion-document:before, +.ion-document-text:before, +.ion-drag:before, +.ion-earth:before, +.ion-easel:before, +.ion-edit:before, +.ion-egg:before, +.ion-eject:before, +.ion-email:before, +.ion-email-unread:before, +.ion-erlenmeyer-flask:before, +.ion-erlenmeyer-flask-bubbles:before, +.ion-eye:before, +.ion-eye-disabled:before, +.ion-female:before, +.ion-filing:before, +.ion-film-marker:before, +.ion-fireball:before, +.ion-flag:before, +.ion-flame:before, +.ion-flash:before, +.ion-flash-off:before, +.ion-folder:before, +.ion-fork:before, +.ion-fork-repo:before, +.ion-forward:before, +.ion-funnel:before, +.ion-gear-a:before, +.ion-gear-b:before, +.ion-grid:before, +.ion-hammer:before, +.ion-happy:before, +.ion-happy-outline:before, +.ion-headphone:before, +.ion-heart:before, +.ion-heart-broken:before, +.ion-help:before, +.ion-help-buoy:before, +.ion-help-circled:before, +.ion-home:before, +.ion-icecream:before, +.ion-image:before, +.ion-images:before, +.ion-information:before, +.ion-information-circled:before, +.ion-ionic:before, +.ion-ios-alarm:before, +.ion-ios-alarm-outline:before, +.ion-ios-albums:before, +.ion-ios-albums-outline:before, +.ion-ios-americanfootball:before, +.ion-ios-americanfootball-outline:before, +.ion-ios-analytics:before, +.ion-ios-analytics-outline:before, +.ion-ios-arrow-back:before, +.ion-ios-arrow-down:before, +.ion-ios-arrow-forward:before, +.ion-ios-arrow-left:before, +.ion-ios-arrow-right:before, +.ion-ios-arrow-thin-down:before, +.ion-ios-arrow-thin-left:before, +.ion-ios-arrow-thin-right:before, +.ion-ios-arrow-thin-up:before, +.ion-ios-arrow-up:before, +.ion-ios-at:before, +.ion-ios-at-outline:before, +.ion-ios-barcode:before, +.ion-ios-barcode-outline:before, +.ion-ios-baseball:before, +.ion-ios-baseball-outline:before, +.ion-ios-basketball:before, +.ion-ios-basketball-outline:before, +.ion-ios-bell:before, +.ion-ios-bell-outline:before, +.ion-ios-body:before, +.ion-ios-body-outline:before, +.ion-ios-bolt:before, +.ion-ios-bolt-outline:before, +.ion-ios-book:before, +.ion-ios-book-outline:before, +.ion-ios-bookmarks:before, +.ion-ios-bookmarks-outline:before, +.ion-ios-box:before, +.ion-ios-box-outline:before, +.ion-ios-briefcase:before, +.ion-ios-briefcase-outline:before, +.ion-ios-browsers:before, +.ion-ios-browsers-outline:before, +.ion-ios-calculator:before, +.ion-ios-calculator-outline:before, +.ion-ios-calendar:before, +.ion-ios-calendar-outline:before, +.ion-ios-camera:before, +.ion-ios-camera-outline:before, +.ion-ios-cart:before, +.ion-ios-cart-outline:before, +.ion-ios-chatboxes:before, +.ion-ios-chatboxes-outline:before, +.ion-ios-chatbubble:before, +.ion-ios-chatbubble-outline:before, +.ion-ios-checkmark:before, +.ion-ios-checkmark-empty:before, +.ion-ios-checkmark-outline:before, +.ion-ios-circle-filled:before, +.ion-ios-circle-outline:before, +.ion-ios-clock:before, +.ion-ios-clock-outline:before, +.ion-ios-close:before, +.ion-ios-close-empty:before, +.ion-ios-close-outline:before, +.ion-ios-cloud:before, +.ion-ios-cloud-download:before, +.ion-ios-cloud-download-outline:before, +.ion-ios-cloud-outline:before, +.ion-ios-cloud-upload:before, +.ion-ios-cloud-upload-outline:before, +.ion-ios-cloudy:before, +.ion-ios-cloudy-night:before, +.ion-ios-cloudy-night-outline:before, +.ion-ios-cloudy-outline:before, +.ion-ios-cog:before, +.ion-ios-cog-outline:before, +.ion-ios-color-filter:before, +.ion-ios-color-filter-outline:before, +.ion-ios-color-wand:before, +.ion-ios-color-wand-outline:before, +.ion-ios-compose:before, +.ion-ios-compose-outline:before, +.ion-ios-contact:before, +.ion-ios-contact-outline:before, +.ion-ios-copy:before, +.ion-ios-copy-outline:before, +.ion-ios-crop:before, +.ion-ios-crop-strong:before, +.ion-ios-download:before, +.ion-ios-download-outline:before, +.ion-ios-drag:before, +.ion-ios-email:before, +.ion-ios-email-outline:before, +.ion-ios-eye:before, +.ion-ios-eye-outline:before, +.ion-ios-fastforward:before, +.ion-ios-fastforward-outline:before, +.ion-ios-filing:before, +.ion-ios-filing-outline:before, +.ion-ios-film:before, +.ion-ios-film-outline:before, +.ion-ios-flag:before, +.ion-ios-flag-outline:before, +.ion-ios-flame:before, +.ion-ios-flame-outline:before, +.ion-ios-flask:before, +.ion-ios-flask-outline:before, +.ion-ios-flower:before, +.ion-ios-flower-outline:before, +.ion-ios-folder:before, +.ion-ios-folder-outline:before, +.ion-ios-football:before, +.ion-ios-football-outline:before, +.ion-ios-game-controller-a:before, +.ion-ios-game-controller-a-outline:before, +.ion-ios-game-controller-b:before, +.ion-ios-game-controller-b-outline:before, +.ion-ios-gear:before, +.ion-ios-gear-outline:before, +.ion-ios-glasses:before, +.ion-ios-glasses-outline:before, +.ion-ios-grid-view:before, +.ion-ios-grid-view-outline:before, +.ion-ios-heart:before, +.ion-ios-heart-outline:before, +.ion-ios-help:before, +.ion-ios-help-empty:before, +.ion-ios-help-outline:before, +.ion-ios-home:before, +.ion-ios-home-outline:before, +.ion-ios-infinite:before, +.ion-ios-infinite-outline:before, +.ion-ios-information:before, +.ion-ios-information-empty:before, +.ion-ios-information-outline:before, +.ion-ios-ionic-outline:before, +.ion-ios-keypad:before, +.ion-ios-keypad-outline:before, +.ion-ios-lightbulb:before, +.ion-ios-lightbulb-outline:before, +.ion-ios-list:before, +.ion-ios-list-outline:before, +.ion-ios-location:before, +.ion-ios-location-outline:before, +.ion-ios-locked:before, +.ion-ios-locked-outline:before, +.ion-ios-loop:before, +.ion-ios-loop-strong:before, +.ion-ios-medical:before, +.ion-ios-medical-outline:before, +.ion-ios-medkit:before, +.ion-ios-medkit-outline:before, +.ion-ios-mic:before, +.ion-ios-mic-off:before, +.ion-ios-mic-outline:before, +.ion-ios-minus:before, +.ion-ios-minus-empty:before, +.ion-ios-minus-outline:before, +.ion-ios-monitor:before, +.ion-ios-monitor-outline:before, +.ion-ios-moon:before, +.ion-ios-moon-outline:before, +.ion-ios-more:before, +.ion-ios-more-outline:before, +.ion-ios-musical-note:before, +.ion-ios-musical-notes:before, +.ion-ios-navigate:before, +.ion-ios-navigate-outline:before, +.ion-ios-nutrition:before, +.ion-ios-nutrition-outline:before, +.ion-ios-paper:before, +.ion-ios-paper-outline:before, +.ion-ios-paperplane:before, +.ion-ios-paperplane-outline:before, +.ion-ios-partlysunny:before, +.ion-ios-partlysunny-outline:before, +.ion-ios-pause:before, +.ion-ios-pause-outline:before, +.ion-ios-paw:before, +.ion-ios-paw-outline:before, +.ion-ios-people:before, +.ion-ios-people-outline:before, +.ion-ios-person:before, +.ion-ios-person-outline:before, +.ion-ios-personadd:before, +.ion-ios-personadd-outline:before, +.ion-ios-photos:before, +.ion-ios-photos-outline:before, +.ion-ios-pie:before, +.ion-ios-pie-outline:before, +.ion-ios-pint:before, +.ion-ios-pint-outline:before, +.ion-ios-play:before, +.ion-ios-play-outline:before, +.ion-ios-plus:before, +.ion-ios-plus-empty:before, +.ion-ios-plus-outline:before, +.ion-ios-pricetag:before, +.ion-ios-pricetag-outline:before, +.ion-ios-pricetags:before, +.ion-ios-pricetags-outline:before, +.ion-ios-printer:before, +.ion-ios-printer-outline:before, +.ion-ios-pulse:before, +.ion-ios-pulse-strong:before, +.ion-ios-rainy:before, +.ion-ios-rainy-outline:before, +.ion-ios-recording:before, +.ion-ios-recording-outline:before, +.ion-ios-redo:before, +.ion-ios-redo-outline:before, +.ion-ios-refresh:before, +.ion-ios-refresh-empty:before, +.ion-ios-refresh-outline:before, +.ion-ios-reload:before, +.ion-ios-reverse-camera:before, +.ion-ios-reverse-camera-outline:before, +.ion-ios-rewind:before, +.ion-ios-rewind-outline:before, +.ion-ios-rose:before, +.ion-ios-rose-outline:before, +.ion-ios-search:before, +.ion-ios-search-strong:before, +.ion-ios-settings:before, +.ion-ios-settings-strong:before, +.ion-ios-shuffle:before, +.ion-ios-shuffle-strong:before, +.ion-ios-skipbackward:before, +.ion-ios-skipbackward-outline:before, +.ion-ios-skipforward:before, +.ion-ios-skipforward-outline:before, +.ion-ios-snowy:before, +.ion-ios-speedometer:before, +.ion-ios-speedometer-outline:before, +.ion-ios-star:before, +.ion-ios-star-half:before, +.ion-ios-star-outline:before, +.ion-ios-stopwatch:before, +.ion-ios-stopwatch-outline:before, +.ion-ios-sunny:before, +.ion-ios-sunny-outline:before, +.ion-ios-telephone:before, +.ion-ios-telephone-outline:before, +.ion-ios-tennisball:before, +.ion-ios-tennisball-outline:before, +.ion-ios-thunderstorm:before, +.ion-ios-thunderstorm-outline:before, +.ion-ios-time:before, +.ion-ios-time-outline:before, +.ion-ios-timer:before, +.ion-ios-timer-outline:before, +.ion-ios-toggle:before, +.ion-ios-toggle-outline:before, +.ion-ios-trash:before, +.ion-ios-trash-outline:before, +.ion-ios-undo:before, +.ion-ios-undo-outline:before, +.ion-ios-unlocked:before, +.ion-ios-unlocked-outline:before, +.ion-ios-upload:before, +.ion-ios-upload-outline:before, +.ion-ios-videocam:before, +.ion-ios-videocam-outline:before, +.ion-ios-volume-high:before, +.ion-ios-volume-low:before, +.ion-ios-wineglass:before, +.ion-ios-wineglass-outline:before, +.ion-ios-world:before, +.ion-ios-world-outline:before, +.ion-ipad:before, +.ion-iphone:before, +.ion-ipod:before, +.ion-jet:before, +.ion-key:before, +.ion-knife:before, +.ion-laptop:before, +.ion-leaf:before, +.ion-levels:before, +.ion-lightbulb:before, +.ion-link:before, +.ion-load-a:before, +.ion-load-b:before, +.ion-load-c:before, +.ion-load-d:before, +.ion-location:before, +.ion-lock-combination:before, +.ion-locked:before, +.ion-log-in:before, +.ion-log-out:before, +.ion-loop:before, +.ion-magnet:before, +.ion-male:before, +.ion-man:before, +.ion-map:before, +.ion-medkit:before, +.ion-merge:before, +.ion-mic-a:before, +.ion-mic-b:before, +.ion-mic-c:before, +.ion-minus:before, +.ion-minus-circled:before, +.ion-minus-round:before, +.ion-model-s:before, +.ion-monitor:before, +.ion-more:before, +.ion-mouse:before, +.ion-music-note:before, +.ion-navicon:before, +.ion-navicon-round:before, +.ion-navigate:before, +.ion-network:before, +.ion-no-smoking:before, +.ion-nuclear:before, +.ion-outlet:before, +.ion-paintbrush:before, +.ion-paintbucket:before, +.ion-paper-airplane:before, +.ion-paperclip:before, +.ion-pause:before, +.ion-person:before, +.ion-person-add:before, +.ion-person-stalker:before, +.ion-pie-graph:before, +.ion-pin:before, +.ion-pinpoint:before, +.ion-pizza:before, +.ion-plane:before, +.ion-planet:before, +.ion-play:before, +.ion-playstation:before, +.ion-plus:before, +.ion-plus-circled:before, +.ion-plus-round:before, +.ion-podium:before, +.ion-pound:before, +.ion-power:before, +.ion-pricetag:before, +.ion-pricetags:before, +.ion-printer:before, +.ion-pull-request:before, +.ion-qr-scanner:before, +.ion-quote:before, +.ion-radio-waves:before, +.ion-record:before, +.ion-refresh:before, +.ion-reply:before, +.ion-reply-all:before, +.ion-ribbon-a:before, +.ion-ribbon-b:before, +.ion-sad:before, +.ion-sad-outline:before, +.ion-scissors:before, +.ion-search:before, +.ion-settings:before, +.ion-share:before, +.ion-shuffle:before, +.ion-skip-backward:before, +.ion-skip-forward:before, +.ion-social-android:before, +.ion-social-android-outline:before, +.ion-social-angular:before, +.ion-social-angular-outline:before, +.ion-social-apple:before, +.ion-social-apple-outline:before, +.ion-social-bitcoin:before, +.ion-social-bitcoin-outline:before, +.ion-social-buffer:before, +.ion-social-buffer-outline:before, +.ion-social-chrome:before, +.ion-social-chrome-outline:before, +.ion-social-codepen:before, +.ion-social-codepen-outline:before, +.ion-social-css3:before, +.ion-social-css3-outline:before, +.ion-social-designernews:before, +.ion-social-designernews-outline:before, +.ion-social-dribbble:before, +.ion-social-dribbble-outline:before, +.ion-social-dropbox:before, +.ion-social-dropbox-outline:before, +.ion-social-euro:before, +.ion-social-euro-outline:before, +.ion-social-facebook:before, +.ion-social-facebook-outline:before, +.ion-social-foursquare:before, +.ion-social-foursquare-outline:before, +.ion-social-freebsd-devil:before, +.ion-social-github:before, +.ion-social-github-outline:before, +.ion-social-google:before, +.ion-social-google-outline:before, +.ion-social-googleplus:before, +.ion-social-googleplus-outline:before, +.ion-social-hackernews:before, +.ion-social-hackernews-outline:before, +.ion-social-html5:before, +.ion-social-html5-outline:before, +.ion-social-instagram:before, +.ion-social-instagram-outline:before, +.ion-social-javascript:before, +.ion-social-javascript-outline:before, +.ion-social-linkedin:before, +.ion-social-linkedin-outline:before, +.ion-social-markdown:before, +.ion-social-nodejs:before, +.ion-social-octocat:before, +.ion-social-pinterest:before, +.ion-social-pinterest-outline:before, +.ion-social-python:before, +.ion-social-reddit:before, +.ion-social-reddit-outline:before, +.ion-social-rss:before, +.ion-social-rss-outline:before, +.ion-social-sass:before, +.ion-social-skype:before, +.ion-social-skype-outline:before, +.ion-social-snapchat:before, +.ion-social-snapchat-outline:before, +.ion-social-tumblr:before, +.ion-social-tumblr-outline:before, +.ion-social-tux:before, +.ion-social-twitch:before, +.ion-social-twitch-outline:before, +.ion-social-twitter:before, +.ion-social-twitter-outline:before, +.ion-social-usd:before, +.ion-social-usd-outline:before, +.ion-social-vimeo:before, +.ion-social-vimeo-outline:before, +.ion-social-whatsapp:before, +.ion-social-whatsapp-outline:before, +.ion-social-windows:before, +.ion-social-windows-outline:before, +.ion-social-wordpress:before, +.ion-social-wordpress-outline:before, +.ion-social-yahoo:before, +.ion-social-yahoo-outline:before, +.ion-social-yen:before, +.ion-social-yen-outline:before, +.ion-social-youtube:before, +.ion-social-youtube-outline:before, +.ion-soup-can:before, +.ion-soup-can-outline:before, +.ion-speakerphone:before, +.ion-speedometer:before, +.ion-spoon:before, +.ion-star:before, +.ion-stats-bars:before, +.ion-steam:before, +.ion-stop:before, +.ion-thermometer:before, +.ion-thumbsdown:before, +.ion-thumbsup:before, +.ion-toggle:before, +.ion-toggle-filled:before, +.ion-transgender:before, +.ion-trash-a:before, +.ion-trash-b:before, +.ion-trophy:before, +.ion-tshirt:before, +.ion-tshirt-outline:before, +.ion-umbrella:before, +.ion-university:before, +.ion-unlocked:before, +.ion-upload:before, +.ion-usb:before, +.ion-videocamera:before, +.ion-volume-high:before, +.ion-volume-low:before, +.ion-volume-medium:before, +.ion-volume-mute:before, +.ion-wand:before, +.ion-waterdrop:before, +.ion-wifi:before, +.ion-wineglass:before, +.ion-woman:before, +.ion-wrench:before, +.ion-xbox:before { + display: inline-block; + font-family: "Ionicons"; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + text-rendering: auto; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } + +.ion-alert:before { + content: ""; } + +.ion-alert-circled:before { + content: ""; } + +.ion-android-add:before { + content: ""; } + +.ion-android-add-circle:before { + content: ""; } + +.ion-android-alarm-clock:before { + content: ""; } + +.ion-android-alert:before { + content: ""; } + +.ion-android-apps:before { + content: ""; } + +.ion-android-archive:before { + content: ""; } + +.ion-android-arrow-back:before { + content: ""; } + +.ion-android-arrow-down:before { + content: ""; } + +.ion-android-arrow-dropdown:before { + content: ""; } + +.ion-android-arrow-dropdown-circle:before { + content: ""; } + +.ion-android-arrow-dropleft:before { + content: ""; } + +.ion-android-arrow-dropleft-circle:before { + content: ""; } + +.ion-android-arrow-dropright:before { + content: ""; } + +.ion-android-arrow-dropright-circle:before { + content: ""; } + +.ion-android-arrow-dropup:before { + content: ""; } + +.ion-android-arrow-dropup-circle:before { + content: ""; } + +.ion-android-arrow-forward:before { + content: ""; } + +.ion-android-arrow-up:before { + content: ""; } + +.ion-android-attach:before { + content: ""; } + +.ion-android-bar:before { + content: ""; } + +.ion-android-bicycle:before { + content: ""; } + +.ion-android-boat:before { + content: ""; } + +.ion-android-bookmark:before { + content: ""; } + +.ion-android-bulb:before { + content: ""; } + +.ion-android-bus:before { + content: ""; } + +.ion-android-calendar:before { + content: ""; } + +.ion-android-call:before { + content: ""; } + +.ion-android-camera:before { + content: ""; } + +.ion-android-cancel:before { + content: ""; } + +.ion-android-car:before { + content: ""; } + +.ion-android-cart:before { + content: ""; } + +.ion-android-chat:before { + content: ""; } + +.ion-android-checkbox:before { + content: ""; } + +.ion-android-checkbox-blank:before { + content: ""; } + +.ion-android-checkbox-outline:before { + content: ""; } + +.ion-android-checkbox-outline-blank:before { + content: ""; } + +.ion-android-checkmark-circle:before { + content: ""; } + +.ion-android-clipboard:before { + content: ""; } + +.ion-android-close:before { + content: ""; } + +.ion-android-cloud:before { + content: ""; } + +.ion-android-cloud-circle:before { + content: ""; } + +.ion-android-cloud-done:before { + content: ""; } + +.ion-android-cloud-outline:before { + content: ""; } + +.ion-android-color-palette:before { + content: ""; } + +.ion-android-compass:before { + content: ""; } + +.ion-android-contact:before { + content: ""; } + +.ion-android-contacts:before { + content: ""; } + +.ion-android-contract:before { + content: ""; } + +.ion-android-create:before { + content: ""; } + +.ion-android-delete:before { + content: ""; } + +.ion-android-desktop:before { + content: ""; } + +.ion-android-document:before { + content: ""; } + +.ion-android-done:before { + content: ""; } + +.ion-android-done-all:before { + content: ""; } + +.ion-android-download:before { + content: ""; } + +.ion-android-drafts:before { + content: ""; } + +.ion-android-exit:before { + content: ""; } + +.ion-android-expand:before { + content: ""; } + +.ion-android-favorite:before { + content: ""; } + +.ion-android-favorite-outline:before { + content: ""; } + +.ion-android-film:before { + content: ""; } + +.ion-android-folder:before { + content: ""; } + +.ion-android-folder-open:before { + content: ""; } + +.ion-android-funnel:before { + content: ""; } + +.ion-android-globe:before { + content: ""; } + +.ion-android-hand:before { + content: ""; } + +.ion-android-hangout:before { + content: ""; } + +.ion-android-happy:before { + content: ""; } + +.ion-android-home:before { + content: ""; } + +.ion-android-image:before { + content: ""; } + +.ion-android-laptop:before { + content: ""; } + +.ion-android-list:before { + content: ""; } + +.ion-android-locate:before { + content: ""; } + +.ion-android-lock:before { + content: ""; } + +.ion-android-mail:before { + content: ""; } + +.ion-android-map:before { + content: ""; } + +.ion-android-menu:before { + content: ""; } + +.ion-android-microphone:before { + content: ""; } + +.ion-android-microphone-off:before { + content: ""; } + +.ion-android-more-horizontal:before { + content: ""; } + +.ion-android-more-vertical:before { + content: ""; } + +.ion-android-navigate:before { + content: ""; } + +.ion-android-notifications:before { + content: ""; } + +.ion-android-notifications-none:before { + content: ""; } + +.ion-android-notifications-off:before { + content: ""; } + +.ion-android-open:before { + content: ""; } + +.ion-android-options:before { + content: ""; } + +.ion-android-people:before { + content: ""; } + +.ion-android-person:before { + content: ""; } + +.ion-android-person-add:before { + content: ""; } + +.ion-android-phone-landscape:before { + content: ""; } + +.ion-android-phone-portrait:before { + content: ""; } + +.ion-android-pin:before { + content: ""; } + +.ion-android-plane:before { + content: ""; } + +.ion-android-playstore:before { + content: ""; } + +.ion-android-print:before { + content: ""; } + +.ion-android-radio-button-off:before { + content: ""; } + +.ion-android-radio-button-on:before { + content: ""; } + +.ion-android-refresh:before { + content: ""; } + +.ion-android-remove:before { + content: ""; } + +.ion-android-remove-circle:before { + content: ""; } + +.ion-android-restaurant:before { + content: ""; } + +.ion-android-sad:before { + content: ""; } + +.ion-android-search:before { + content: ""; } + +.ion-android-send:before { + content: ""; } + +.ion-android-settings:before { + content: ""; } + +.ion-android-share:before { + content: ""; } + +.ion-android-share-alt:before { + content: ""; } + +.ion-android-star:before { + content: ""; } + +.ion-android-star-half:before { + content: ""; } + +.ion-android-star-outline:before { + content: ""; } + +.ion-android-stopwatch:before { + content: ""; } + +.ion-android-subway:before { + content: ""; } + +.ion-android-sunny:before { + content: ""; } + +.ion-android-sync:before { + content: ""; } + +.ion-android-textsms:before { + content: ""; } + +.ion-android-time:before { + content: ""; } + +.ion-android-train:before { + content: ""; } + +.ion-android-unlock:before { + content: ""; } + +.ion-android-upload:before { + content: ""; } + +.ion-android-volume-down:before { + content: ""; } + +.ion-android-volume-mute:before { + content: ""; } + +.ion-android-volume-off:before { + content: ""; } + +.ion-android-volume-up:before { + content: ""; } + +.ion-android-walk:before { + content: ""; } + +.ion-android-warning:before { + content: ""; } + +.ion-android-watch:before { + content: ""; } + +.ion-android-wifi:before { + content: ""; } + +.ion-aperture:before { + content: ""; } + +.ion-archive:before { + content: ""; } + +.ion-arrow-down-a:before { + content: ""; } + +.ion-arrow-down-b:before { + content: ""; } + +.ion-arrow-down-c:before { + content: ""; } + +.ion-arrow-expand:before { + content: ""; } + +.ion-arrow-graph-down-left:before { + content: ""; } + +.ion-arrow-graph-down-right:before { + content: ""; } + +.ion-arrow-graph-up-left:before { + content: ""; } + +.ion-arrow-graph-up-right:before { + content: ""; } + +.ion-arrow-left-a:before { + content: ""; } + +.ion-arrow-left-b:before { + content: ""; } + +.ion-arrow-left-c:before { + content: ""; } + +.ion-arrow-move:before { + content: ""; } + +.ion-arrow-resize:before { + content: ""; } + +.ion-arrow-return-left:before { + content: ""; } + +.ion-arrow-return-right:before { + content: ""; } + +.ion-arrow-right-a:before { + content: ""; } + +.ion-arrow-right-b:before { + content: ""; } + +.ion-arrow-right-c:before { + content: ""; } + +.ion-arrow-shrink:before { + content: ""; } + +.ion-arrow-swap:before { + content: ""; } + +.ion-arrow-up-a:before { + content: ""; } + +.ion-arrow-up-b:before { + content: ""; } + +.ion-arrow-up-c:before { + content: ""; } + +.ion-asterisk:before { + content: ""; } + +.ion-at:before { + content: ""; } + +.ion-backspace:before { + content: ""; } + +.ion-backspace-outline:before { + content: ""; } + +.ion-bag:before { + content: ""; } + +.ion-battery-charging:before { + content: ""; } + +.ion-battery-empty:before { + content: ""; } + +.ion-battery-full:before { + content: ""; } + +.ion-battery-half:before { + content: ""; } + +.ion-battery-low:before { + content: ""; } + +.ion-beaker:before { + content: ""; } + +.ion-beer:before { + content: ""; } + +.ion-bluetooth:before { + content: ""; } + +.ion-bonfire:before { + content: ""; } + +.ion-bookmark:before { + content: ""; } + +.ion-bowtie:before { + content: ""; } + +.ion-briefcase:before { + content: ""; } + +.ion-bug:before { + content: ""; } + +.ion-calculator:before { + content: ""; } + +.ion-calendar:before { + content: ""; } + +.ion-camera:before { + content: ""; } + +.ion-card:before { + content: ""; } + +.ion-cash:before { + content: ""; } + +.ion-chatbox:before { + content: ""; } + +.ion-chatbox-working:before { + content: ""; } + +.ion-chatboxes:before { + content: ""; } + +.ion-chatbubble:before { + content: ""; } + +.ion-chatbubble-working:before { + content: ""; } + +.ion-chatbubbles:before { + content: ""; } + +.ion-checkmark:before { + content: ""; } + +.ion-checkmark-circled:before { + content: ""; } + +.ion-checkmark-round:before { + content: ""; } + +.ion-chevron-down:before { + content: ""; } + +.ion-chevron-left:before { + content: ""; } + +.ion-chevron-right:before { + content: ""; } + +.ion-chevron-up:before { + content: ""; } + +.ion-clipboard:before { + content: ""; } + +.ion-clock:before { + content: ""; } + +.ion-close:before { + content: ""; } + +.ion-close-circled:before { + content: ""; } + +.ion-close-round:before { + content: ""; } + +.ion-closed-captioning:before { + content: ""; } + +.ion-cloud:before { + content: ""; } + +.ion-code:before { + content: ""; } + +.ion-code-download:before { + content: ""; } + +.ion-code-working:before { + content: ""; } + +.ion-coffee:before { + content: ""; } + +.ion-compass:before { + content: ""; } + +.ion-compose:before { + content: ""; } + +.ion-connection-bars:before { + content: ""; } + +.ion-contrast:before { + content: ""; } + +.ion-crop:before { + content: ""; } + +.ion-cube:before { + content: ""; } + +.ion-disc:before { + content: ""; } + +.ion-document:before { + content: ""; } + +.ion-document-text:before { + content: ""; } + +.ion-drag:before { + content: ""; } + +.ion-earth:before { + content: ""; } + +.ion-easel:before { + content: ""; } + +.ion-edit:before { + content: ""; } + +.ion-egg:before { + content: ""; } + +.ion-eject:before { + content: ""; } + +.ion-email:before { + content: ""; } + +.ion-email-unread:before { + content: ""; } + +.ion-erlenmeyer-flask:before { + content: ""; } + +.ion-erlenmeyer-flask-bubbles:before { + content: ""; } + +.ion-eye:before { + content: ""; } + +.ion-eye-disabled:before { + content: ""; } + +.ion-female:before { + content: ""; } + +.ion-filing:before { + content: ""; } + +.ion-film-marker:before { + content: ""; } + +.ion-fireball:before { + content: ""; } + +.ion-flag:before { + content: ""; } + +.ion-flame:before { + content: ""; } + +.ion-flash:before { + content: ""; } + +.ion-flash-off:before { + content: ""; } + +.ion-folder:before { + content: ""; } + +.ion-fork:before { + content: ""; } + +.ion-fork-repo:before { + content: ""; } + +.ion-forward:before { + content: ""; } + +.ion-funnel:before { + content: ""; } + +.ion-gear-a:before { + content: ""; } + +.ion-gear-b:before { + content: ""; } + +.ion-grid:before { + content: ""; } + +.ion-hammer:before { + content: ""; } + +.ion-happy:before { + content: ""; } + +.ion-happy-outline:before { + content: ""; } + +.ion-headphone:before { + content: ""; } + +.ion-heart:before { + content: ""; } + +.ion-heart-broken:before { + content: ""; } + +.ion-help:before { + content: ""; } + +.ion-help-buoy:before { + content: ""; } + +.ion-help-circled:before { + content: ""; } + +.ion-home:before { + content: ""; } + +.ion-icecream:before { + content: ""; } + +.ion-image:before { + content: ""; } + +.ion-images:before { + content: ""; } + +.ion-information:before { + content: ""; } + +.ion-information-circled:before { + content: ""; } + +.ion-ionic:before { + content: ""; } + +.ion-ios-alarm:before { + content: ""; } + +.ion-ios-alarm-outline:before { + content: ""; } + +.ion-ios-albums:before { + content: ""; } + +.ion-ios-albums-outline:before { + content: ""; } + +.ion-ios-americanfootball:before { + content: ""; } + +.ion-ios-americanfootball-outline:before { + content: ""; } + +.ion-ios-analytics:before { + content: ""; } + +.ion-ios-analytics-outline:before { + content: ""; } + +.ion-ios-arrow-back:before { + content: ""; } + +.ion-ios-arrow-down:before { + content: ""; } + +.ion-ios-arrow-forward:before { + content: ""; } + +.ion-ios-arrow-left:before { + content: ""; } + +.ion-ios-arrow-right:before { + content: ""; } + +.ion-ios-arrow-thin-down:before { + content: ""; } + +.ion-ios-arrow-thin-left:before { + content: ""; } + +.ion-ios-arrow-thin-right:before { + content: ""; } + +.ion-ios-arrow-thin-up:before { + content: ""; } + +.ion-ios-arrow-up:before { + content: ""; } + +.ion-ios-at:before { + content: ""; } + +.ion-ios-at-outline:before { + content: ""; } + +.ion-ios-barcode:before { + content: ""; } + +.ion-ios-barcode-outline:before { + content: ""; } + +.ion-ios-baseball:before { + content: ""; } + +.ion-ios-baseball-outline:before { + content: ""; } + +.ion-ios-basketball:before { + content: ""; } + +.ion-ios-basketball-outline:before { + content: ""; } + +.ion-ios-bell:before { + content: ""; } + +.ion-ios-bell-outline:before { + content: ""; } + +.ion-ios-body:before { + content: ""; } + +.ion-ios-body-outline:before { + content: ""; } + +.ion-ios-bolt:before { + content: ""; } + +.ion-ios-bolt-outline:before { + content: ""; } + +.ion-ios-book:before { + content: ""; } + +.ion-ios-book-outline:before { + content: ""; } + +.ion-ios-bookmarks:before { + content: ""; } + +.ion-ios-bookmarks-outline:before { + content: ""; } + +.ion-ios-box:before { + content: ""; } + +.ion-ios-box-outline:before { + content: ""; } + +.ion-ios-briefcase:before { + content: ""; } + +.ion-ios-briefcase-outline:before { + content: ""; } + +.ion-ios-browsers:before { + content: ""; } + +.ion-ios-browsers-outline:before { + content: ""; } + +.ion-ios-calculator:before { + content: ""; } + +.ion-ios-calculator-outline:before { + content: ""; } + +.ion-ios-calendar:before { + content: ""; } + +.ion-ios-calendar-outline:before { + content: ""; } + +.ion-ios-camera:before { + content: ""; } + +.ion-ios-camera-outline:before { + content: ""; } + +.ion-ios-cart:before { + content: ""; } + +.ion-ios-cart-outline:before { + content: ""; } + +.ion-ios-chatboxes:before { + content: ""; } + +.ion-ios-chatboxes-outline:before { + content: ""; } + +.ion-ios-chatbubble:before { + content: ""; } + +.ion-ios-chatbubble-outline:before { + content: ""; } + +.ion-ios-checkmark:before { + content: ""; } + +.ion-ios-checkmark-empty:before { + content: ""; } + +.ion-ios-checkmark-outline:before { + content: ""; } + +.ion-ios-circle-filled:before { + content: ""; } + +.ion-ios-circle-outline:before { + content: ""; } + +.ion-ios-clock:before { + content: ""; } + +.ion-ios-clock-outline:before { + content: ""; } + +.ion-ios-close:before { + content: ""; } + +.ion-ios-close-empty:before { + content: ""; } + +.ion-ios-close-outline:before { + content: ""; } + +.ion-ios-cloud:before { + content: ""; } + +.ion-ios-cloud-download:before { + content: ""; } + +.ion-ios-cloud-download-outline:before { + content: ""; } + +.ion-ios-cloud-outline:before { + content: ""; } + +.ion-ios-cloud-upload:before { + content: ""; } + +.ion-ios-cloud-upload-outline:before { + content: ""; } + +.ion-ios-cloudy:before { + content: ""; } + +.ion-ios-cloudy-night:before { + content: ""; } + +.ion-ios-cloudy-night-outline:before { + content: ""; } + +.ion-ios-cloudy-outline:before { + content: ""; } + +.ion-ios-cog:before { + content: ""; } + +.ion-ios-cog-outline:before { + content: ""; } + +.ion-ios-color-filter:before { + content: ""; } + +.ion-ios-color-filter-outline:before { + content: ""; } + +.ion-ios-color-wand:before { + content: ""; } + +.ion-ios-color-wand-outline:before { + content: ""; } + +.ion-ios-compose:before { + content: ""; } + +.ion-ios-compose-outline:before { + content: ""; } + +.ion-ios-contact:before { + content: ""; } + +.ion-ios-contact-outline:before { + content: ""; } + +.ion-ios-copy:before { + content: ""; } + +.ion-ios-copy-outline:before { + content: ""; } + +.ion-ios-crop:before { + content: ""; } + +.ion-ios-crop-strong:before { + content: ""; } + +.ion-ios-download:before { + content: ""; } + +.ion-ios-download-outline:before { + content: ""; } + +.ion-ios-drag:before { + content: ""; } + +.ion-ios-email:before { + content: ""; } + +.ion-ios-email-outline:before { + content: ""; } + +.ion-ios-eye:before { + content: ""; } + +.ion-ios-eye-outline:before { + content: ""; } + +.ion-ios-fastforward:before { + content: ""; } + +.ion-ios-fastforward-outline:before { + content: ""; } + +.ion-ios-filing:before { + content: ""; } + +.ion-ios-filing-outline:before { + content: ""; } + +.ion-ios-film:before { + content: ""; } + +.ion-ios-film-outline:before { + content: ""; } + +.ion-ios-flag:before { + content: ""; } + +.ion-ios-flag-outline:before { + content: ""; } + +.ion-ios-flame:before { + content: ""; } + +.ion-ios-flame-outline:before { + content: ""; } + +.ion-ios-flask:before { + content: ""; } + +.ion-ios-flask-outline:before { + content: ""; } + +.ion-ios-flower:before { + content: ""; } + +.ion-ios-flower-outline:before { + content: ""; } + +.ion-ios-folder:before { + content: ""; } + +.ion-ios-folder-outline:before { + content: ""; } + +.ion-ios-football:before { + content: ""; } + +.ion-ios-football-outline:before { + content: ""; } + +.ion-ios-game-controller-a:before { + content: ""; } + +.ion-ios-game-controller-a-outline:before { + content: ""; } + +.ion-ios-game-controller-b:before { + content: ""; } + +.ion-ios-game-controller-b-outline:before { + content: ""; } + +.ion-ios-gear:before { + content: ""; } + +.ion-ios-gear-outline:before { + content: ""; } + +.ion-ios-glasses:before { + content: ""; } + +.ion-ios-glasses-outline:before { + content: ""; } + +.ion-ios-grid-view:before { + content: ""; } + +.ion-ios-grid-view-outline:before { + content: ""; } + +.ion-ios-heart:before { + content: ""; } + +.ion-ios-heart-outline:before { + content: ""; } + +.ion-ios-help:before { + content: ""; } + +.ion-ios-help-empty:before { + content: ""; } + +.ion-ios-help-outline:before { + content: ""; } + +.ion-ios-home:before { + content: ""; } + +.ion-ios-home-outline:before { + content: ""; } + +.ion-ios-infinite:before { + content: ""; } + +.ion-ios-infinite-outline:before { + content: ""; } + +.ion-ios-information:before { + content: ""; } + +.ion-ios-information-empty:before { + content: ""; } + +.ion-ios-information-outline:before { + content: ""; } + +.ion-ios-ionic-outline:before { + content: ""; } + +.ion-ios-keypad:before { + content: ""; } + +.ion-ios-keypad-outline:before { + content: ""; } + +.ion-ios-lightbulb:before { + content: ""; } + +.ion-ios-lightbulb-outline:before { + content: ""; } + +.ion-ios-list:before { + content: ""; } + +.ion-ios-list-outline:before { + content: ""; } + +.ion-ios-location:before { + content: ""; } + +.ion-ios-location-outline:before { + content: ""; } + +.ion-ios-locked:before { + content: ""; } + +.ion-ios-locked-outline:before { + content: ""; } + +.ion-ios-loop:before { + content: ""; } + +.ion-ios-loop-strong:before { + content: ""; } + +.ion-ios-medical:before { + content: ""; } + +.ion-ios-medical-outline:before { + content: ""; } + +.ion-ios-medkit:before { + content: ""; } + +.ion-ios-medkit-outline:before { + content: ""; } + +.ion-ios-mic:before { + content: ""; } + +.ion-ios-mic-off:before { + content: ""; } + +.ion-ios-mic-outline:before { + content: ""; } + +.ion-ios-minus:before { + content: ""; } + +.ion-ios-minus-empty:before { + content: ""; } + +.ion-ios-minus-outline:before { + content: ""; } + +.ion-ios-monitor:before { + content: ""; } + +.ion-ios-monitor-outline:before { + content: ""; } + +.ion-ios-moon:before { + content: ""; } + +.ion-ios-moon-outline:before { + content: ""; } + +.ion-ios-more:before { + content: ""; } + +.ion-ios-more-outline:before { + content: ""; } + +.ion-ios-musical-note:before { + content: ""; } + +.ion-ios-musical-notes:before { + content: ""; } + +.ion-ios-navigate:before { + content: ""; } + +.ion-ios-navigate-outline:before { + content: ""; } + +.ion-ios-nutrition:before { + content: ""; } + +.ion-ios-nutrition-outline:before { + content: ""; } + +.ion-ios-paper:before { + content: ""; } + +.ion-ios-paper-outline:before { + content: ""; } + +.ion-ios-paperplane:before { + content: ""; } + +.ion-ios-paperplane-outline:before { + content: ""; } + +.ion-ios-partlysunny:before { + content: ""; } + +.ion-ios-partlysunny-outline:before { + content: ""; } + +.ion-ios-pause:before { + content: ""; } + +.ion-ios-pause-outline:before { + content: ""; } + +.ion-ios-paw:before { + content: ""; } + +.ion-ios-paw-outline:before { + content: ""; } + +.ion-ios-people:before { + content: ""; } + +.ion-ios-people-outline:before { + content: ""; } + +.ion-ios-person:before { + content: ""; } + +.ion-ios-person-outline:before { + content: ""; } + +.ion-ios-personadd:before { + content: ""; } + +.ion-ios-personadd-outline:before { + content: ""; } + +.ion-ios-photos:before { + content: ""; } + +.ion-ios-photos-outline:before { + content: ""; } + +.ion-ios-pie:before { + content: ""; } + +.ion-ios-pie-outline:before { + content: ""; } + +.ion-ios-pint:before { + content: ""; } + +.ion-ios-pint-outline:before { + content: ""; } + +.ion-ios-play:before { + content: ""; } + +.ion-ios-play-outline:before { + content: ""; } + +.ion-ios-plus:before { + content: ""; } + +.ion-ios-plus-empty:before { + content: ""; } + +.ion-ios-plus-outline:before { + content: ""; } + +.ion-ios-pricetag:before { + content: ""; } + +.ion-ios-pricetag-outline:before { + content: ""; } + +.ion-ios-pricetags:before { + content: ""; } + +.ion-ios-pricetags-outline:before { + content: ""; } + +.ion-ios-printer:before { + content: ""; } + +.ion-ios-printer-outline:before { + content: ""; } + +.ion-ios-pulse:before { + content: ""; } + +.ion-ios-pulse-strong:before { + content: ""; } + +.ion-ios-rainy:before { + content: ""; } + +.ion-ios-rainy-outline:before { + content: ""; } + +.ion-ios-recording:before { + content: ""; } + +.ion-ios-recording-outline:before { + content: ""; } + +.ion-ios-redo:before { + content: ""; } + +.ion-ios-redo-outline:before { + content: ""; } + +.ion-ios-refresh:before { + content: ""; } + +.ion-ios-refresh-empty:before { + content: ""; } + +.ion-ios-refresh-outline:before { + content: ""; } + +.ion-ios-reload:before { + content: ""; } + +.ion-ios-reverse-camera:before { + content: ""; } + +.ion-ios-reverse-camera-outline:before { + content: ""; } + +.ion-ios-rewind:before { + content: ""; } + +.ion-ios-rewind-outline:before { + content: ""; } + +.ion-ios-rose:before { + content: ""; } + +.ion-ios-rose-outline:before { + content: ""; } + +.ion-ios-search:before { + content: ""; } + +.ion-ios-search-strong:before { + content: ""; } + +.ion-ios-settings:before { + content: ""; } + +.ion-ios-settings-strong:before { + content: ""; } + +.ion-ios-shuffle:before { + content: ""; } + +.ion-ios-shuffle-strong:before { + content: ""; } + +.ion-ios-skipbackward:before { + content: ""; } + +.ion-ios-skipbackward-outline:before { + content: ""; } + +.ion-ios-skipforward:before { + content: ""; } + +.ion-ios-skipforward-outline:before { + content: ""; } + +.ion-ios-snowy:before { + content: ""; } + +.ion-ios-speedometer:before { + content: ""; } + +.ion-ios-speedometer-outline:before { + content: ""; } + +.ion-ios-star:before { + content: ""; } + +.ion-ios-star-half:before { + content: ""; } + +.ion-ios-star-outline:before { + content: ""; } + +.ion-ios-stopwatch:before { + content: ""; } + +.ion-ios-stopwatch-outline:before { + content: ""; } + +.ion-ios-sunny:before { + content: ""; } + +.ion-ios-sunny-outline:before { + content: ""; } + +.ion-ios-telephone:before { + content: ""; } + +.ion-ios-telephone-outline:before { + content: ""; } + +.ion-ios-tennisball:before { + content: ""; } + +.ion-ios-tennisball-outline:before { + content: ""; } + +.ion-ios-thunderstorm:before { + content: ""; } + +.ion-ios-thunderstorm-outline:before { + content: ""; } + +.ion-ios-time:before { + content: ""; } + +.ion-ios-time-outline:before { + content: ""; } + +.ion-ios-timer:before { + content: ""; } + +.ion-ios-timer-outline:before { + content: ""; } + +.ion-ios-toggle:before { + content: ""; } + +.ion-ios-toggle-outline:before { + content: ""; } + +.ion-ios-trash:before { + content: ""; } + +.ion-ios-trash-outline:before { + content: ""; } + +.ion-ios-undo:before { + content: ""; } + +.ion-ios-undo-outline:before { + content: ""; } + +.ion-ios-unlocked:before { + content: ""; } + +.ion-ios-unlocked-outline:before { + content: ""; } + +.ion-ios-upload:before { + content: ""; } + +.ion-ios-upload-outline:before { + content: ""; } + +.ion-ios-videocam:before { + content: ""; } + +.ion-ios-videocam-outline:before { + content: ""; } + +.ion-ios-volume-high:before { + content: ""; } + +.ion-ios-volume-low:before { + content: ""; } + +.ion-ios-wineglass:before { + content: ""; } + +.ion-ios-wineglass-outline:before { + content: ""; } + +.ion-ios-world:before { + content: ""; } + +.ion-ios-world-outline:before { + content: ""; } + +.ion-ipad:before { + content: ""; } + +.ion-iphone:before { + content: ""; } + +.ion-ipod:before { + content: ""; } + +.ion-jet:before { + content: ""; } + +.ion-key:before { + content: ""; } + +.ion-knife:before { + content: ""; } + +.ion-laptop:before { + content: ""; } + +.ion-leaf:before { + content: ""; } + +.ion-levels:before { + content: ""; } + +.ion-lightbulb:before { + content: ""; } + +.ion-link:before { + content: ""; } + +.ion-load-a:before { + content: ""; } + +.ion-load-b:before { + content: ""; } + +.ion-load-c:before { + content: ""; } + +.ion-load-d:before { + content: ""; } + +.ion-location:before { + content: ""; } + +.ion-lock-combination:before { + content: ""; } + +.ion-locked:before { + content: ""; } + +.ion-log-in:before { + content: ""; } + +.ion-log-out:before { + content: ""; } + +.ion-loop:before { + content: ""; } + +.ion-magnet:before { + content: ""; } + +.ion-male:before { + content: ""; } + +.ion-man:before { + content: ""; } + +.ion-map:before { + content: ""; } + +.ion-medkit:before { + content: ""; } + +.ion-merge:before { + content: ""; } + +.ion-mic-a:before { + content: ""; } + +.ion-mic-b:before { + content: ""; } + +.ion-mic-c:before { + content: ""; } + +.ion-minus:before { + content: ""; } + +.ion-minus-circled:before { + content: ""; } + +.ion-minus-round:before { + content: ""; } + +.ion-model-s:before { + content: ""; } + +.ion-monitor:before { + content: ""; } + +.ion-more:before { + content: ""; } + +.ion-mouse:before { + content: ""; } + +.ion-music-note:before { + content: ""; } + +.ion-navicon:before { + content: ""; } + +.ion-navicon-round:before { + content: ""; } + +.ion-navigate:before { + content: ""; } + +.ion-network:before { + content: ""; } + +.ion-no-smoking:before { + content: ""; } + +.ion-nuclear:before { + content: ""; } + +.ion-outlet:before { + content: ""; } + +.ion-paintbrush:before { + content: ""; } + +.ion-paintbucket:before { + content: ""; } + +.ion-paper-airplane:before { + content: ""; } + +.ion-paperclip:before { + content: ""; } + +.ion-pause:before { + content: ""; } + +.ion-person:before { + content: ""; } + +.ion-person-add:before { + content: ""; } + +.ion-person-stalker:before { + content: ""; } + +.ion-pie-graph:before { + content: ""; } + +.ion-pin:before { + content: ""; } + +.ion-pinpoint:before { + content: ""; } + +.ion-pizza:before { + content: ""; } + +.ion-plane:before { + content: ""; } + +.ion-planet:before { + content: ""; } + +.ion-play:before { + content: ""; } + +.ion-playstation:before { + content: ""; } + +.ion-plus:before { + content: ""; } + +.ion-plus-circled:before { + content: ""; } + +.ion-plus-round:before { + content: ""; } + +.ion-podium:before { + content: ""; } + +.ion-pound:before { + content: ""; } + +.ion-power:before { + content: ""; } + +.ion-pricetag:before { + content: ""; } + +.ion-pricetags:before { + content: ""; } + +.ion-printer:before { + content: ""; } + +.ion-pull-request:before { + content: ""; } + +.ion-qr-scanner:before { + content: ""; } + +.ion-quote:before { + content: ""; } + +.ion-radio-waves:before { + content: ""; } + +.ion-record:before { + content: ""; } + +.ion-refresh:before { + content: ""; } + +.ion-reply:before { + content: ""; } + +.ion-reply-all:before { + content: ""; } + +.ion-ribbon-a:before { + content: ""; } + +.ion-ribbon-b:before { + content: ""; } + +.ion-sad:before { + content: ""; } + +.ion-sad-outline:before { + content: ""; } + +.ion-scissors:before { + content: ""; } + +.ion-search:before { + content: ""; } + +.ion-settings:before { + content: ""; } + +.ion-share:before { + content: ""; } + +.ion-shuffle:before { + content: ""; } + +.ion-skip-backward:before { + content: ""; } + +.ion-skip-forward:before { + content: ""; } + +.ion-social-android:before { + content: ""; } + +.ion-social-android-outline:before { + content: ""; } + +.ion-social-angular:before { + content: ""; } + +.ion-social-angular-outline:before { + content: ""; } + +.ion-social-apple:before { + content: ""; } + +.ion-social-apple-outline:before { + content: ""; } + +.ion-social-bitcoin:before { + content: ""; } + +.ion-social-bitcoin-outline:before { + content: ""; } + +.ion-social-buffer:before { + content: ""; } + +.ion-social-buffer-outline:before { + content: ""; } + +.ion-social-chrome:before { + content: ""; } + +.ion-social-chrome-outline:before { + content: ""; } + +.ion-social-codepen:before { + content: ""; } + +.ion-social-codepen-outline:before { + content: ""; } + +.ion-social-css3:before { + content: ""; } + +.ion-social-css3-outline:before { + content: ""; } + +.ion-social-designernews:before { + content: ""; } + +.ion-social-designernews-outline:before { + content: ""; } + +.ion-social-dribbble:before { + content: ""; } + +.ion-social-dribbble-outline:before { + content: ""; } + +.ion-social-dropbox:before { + content: ""; } + +.ion-social-dropbox-outline:before { + content: ""; } + +.ion-social-euro:before { + content: ""; } + +.ion-social-euro-outline:before { + content: ""; } + +.ion-social-facebook:before { + content: ""; } + +.ion-social-facebook-outline:before { + content: ""; } + +.ion-social-foursquare:before { + content: ""; } + +.ion-social-foursquare-outline:before { + content: ""; } + +.ion-social-freebsd-devil:before { + content: ""; } + +.ion-social-github:before { + content: ""; } + +.ion-social-github-outline:before { + content: ""; } + +.ion-social-google:before { + content: ""; } + +.ion-social-google-outline:before { + content: ""; } + +.ion-social-googleplus:before { + content: ""; } + +.ion-social-googleplus-outline:before { + content: ""; } + +.ion-social-hackernews:before { + content: ""; } + +.ion-social-hackernews-outline:before { + content: ""; } + +.ion-social-html5:before { + content: ""; } + +.ion-social-html5-outline:before { + content: ""; } + +.ion-social-instagram:before { + content: ""; } + +.ion-social-instagram-outline:before { + content: ""; } + +.ion-social-javascript:before { + content: ""; } + +.ion-social-javascript-outline:before { + content: ""; } + +.ion-social-linkedin:before { + content: ""; } + +.ion-social-linkedin-outline:before { + content: ""; } + +.ion-social-markdown:before { + content: ""; } + +.ion-social-nodejs:before { + content: ""; } + +.ion-social-octocat:before { + content: ""; } + +.ion-social-pinterest:before { + content: ""; } + +.ion-social-pinterest-outline:before { + content: ""; } + +.ion-social-python:before { + content: ""; } + +.ion-social-reddit:before { + content: ""; } + +.ion-social-reddit-outline:before { + content: ""; } + +.ion-social-rss:before { + content: ""; } + +.ion-social-rss-outline:before { + content: ""; } + +.ion-social-sass:before { + content: ""; } + +.ion-social-skype:before { + content: ""; } + +.ion-social-skype-outline:before { + content: ""; } + +.ion-social-snapchat:before { + content: ""; } + +.ion-social-snapchat-outline:before { + content: ""; } + +.ion-social-tumblr:before { + content: ""; } + +.ion-social-tumblr-outline:before { + content: ""; } + +.ion-social-tux:before { + content: ""; } + +.ion-social-twitch:before { + content: ""; } + +.ion-social-twitch-outline:before { + content: ""; } + +.ion-social-twitter:before { + content: ""; } + +.ion-social-twitter-outline:before { + content: ""; } + +.ion-social-usd:before { + content: ""; } + +.ion-social-usd-outline:before { + content: ""; } + +.ion-social-vimeo:before { + content: ""; } + +.ion-social-vimeo-outline:before { + content: ""; } + +.ion-social-whatsapp:before { + content: ""; } + +.ion-social-whatsapp-outline:before { + content: ""; } + +.ion-social-windows:before { + content: ""; } + +.ion-social-windows-outline:before { + content: ""; } + +.ion-social-wordpress:before { + content: ""; } + +.ion-social-wordpress-outline:before { + content: ""; } + +.ion-social-yahoo:before { + content: ""; } + +.ion-social-yahoo-outline:before { + content: ""; } + +.ion-social-yen:before { + content: ""; } + +.ion-social-yen-outline:before { + content: ""; } + +.ion-social-youtube:before { + content: ""; } + +.ion-social-youtube-outline:before { + content: ""; } + +.ion-soup-can:before { + content: ""; } + +.ion-soup-can-outline:before { + content: ""; } + +.ion-speakerphone:before { + content: ""; } + +.ion-speedometer:before { + content: ""; } + +.ion-spoon:before { + content: ""; } + +.ion-star:before { + content: ""; } + +.ion-stats-bars:before { + content: ""; } + +.ion-steam:before { + content: ""; } + +.ion-stop:before { + content: ""; } + +.ion-thermometer:before { + content: ""; } + +.ion-thumbsdown:before { + content: ""; } + +.ion-thumbsup:before { + content: ""; } + +.ion-toggle:before { + content: ""; } + +.ion-toggle-filled:before { + content: ""; } + +.ion-transgender:before { + content: ""; } + +.ion-trash-a:before { + content: ""; } + +.ion-trash-b:before { + content: ""; } + +.ion-trophy:before { + content: ""; } + +.ion-tshirt:before { + content: ""; } + +.ion-tshirt-outline:before { + content: ""; } + +.ion-umbrella:before { + content: ""; } + +.ion-university:before { + content: ""; } + +.ion-unlocked:before { + content: ""; } + +.ion-upload:before { + content: ""; } + +.ion-usb:before { + content: ""; } + +.ion-videocamera:before { + content: ""; } + +.ion-volume-high:before { + content: ""; } + +.ion-volume-low:before { + content: ""; } + +.ion-volume-medium:before { + content: ""; } + +.ion-volume-mute:before { + content: ""; } + +.ion-wand:before { + content: ""; } + +.ion-waterdrop:before { + content: ""; } + +.ion-wifi:before { + content: ""; } + +.ion-wineglass:before { + content: ""; } + +.ion-woman:before { + content: ""; } + +.ion-wrench:before { + content: ""; } + +.ion-xbox:before { + content: ""; } + +/** + * Resets + * -------------------------------------------------- + * Adapted from normalize.css and some reset.css. We don't care even one + * bit about old IE, so we don't need any hacks for that in here. + * + * There are probably other things we could remove here, as well. + * + * normalize.css v2.1.2 | MIT License | git.io/normalize + + * Eric Meyer's Reset CSS v2.0 (http://meyerweb.com/eric/tools/css/reset/) + * http://cssreset.com + */ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, i, u, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, fieldset, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + vertical-align: baseline; + font: inherit; + font-size: 100%; } + +ol, ul { + list-style: none; } + +blockquote, q { + quotes: none; } + +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; } + +/** + * Prevent modern browsers from displaying `audio` without controls. + * Remove excess height in iOS 5 devices. + */ +audio:not([controls]) { + display: none; + height: 0; } + +/** + * Hide the `template` element in IE, Safari, and Firefox < 22. + */ +[hidden], +template { + display: none; } + +script { + display: none !important; } + +/* ========================================================================== + Base + ========================================================================== */ +/** + * 1. Set default font family to sans-serif. + * 2. Prevent iOS text size adjust after orientation change, without disabling + * user zoom. + */ +html { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + font-family: sans-serif; + /* 1 */ + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; + /* 2 */ + -webkit-text-size-adjust: 100%; + /* 2 */ } + +/** + * Remove default margin. + */ +body { + margin: 0; + line-height: 1; } + +/** + * Remove default outlines. + */ +a, +button, +:focus, +a:focus, +button:focus, +a:active, +a:hover { + outline: 0; } + +/* * + * Remove tap highlight color + */ +a { + -webkit-user-drag: none; + -webkit-tap-highlight-color: transparent; + -webkit-tap-highlight-color: transparent; } + a[href]:hover { + cursor: pointer; } + +/* ========================================================================== + Typography + ========================================================================== */ +/** + * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. + */ +b, +strong { + font-weight: bold; } + +/** + * Address styling not present in Safari 5 and Chrome. + */ +dfn { + font-style: italic; } + +/** + * Address differences between Firefox and other browsers. + */ +hr { + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0; } + +/** + * Correct font family set oddly in Safari 5 and Chrome. + */ +code, +kbd, +pre, +samp { + font-size: 1em; + font-family: monospace, serif; } + +/** + * Improve readability of pre-formatted text in all browsers. + */ +pre { + white-space: pre-wrap; } + +/** + * Set consistent quote types. + */ +q { + quotes: "\201C" "\201D" "\2018" "\2019"; } + +/** + * Address inconsistent and variable font size in all browsers. + */ +small { + font-size: 80%; } + +/** + * Prevent `sub` and `sup` affecting `line-height` in all browsers. + */ +sub, +sup { + position: relative; + vertical-align: baseline; + font-size: 75%; + line-height: 0; } + +sup { + top: -0.5em; } + +sub { + bottom: -0.25em; } + +/** + * Define consistent border, margin, and padding. + */ +fieldset { + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; + border: 1px solid #c0c0c0; } + +/** + * 1. Correct `color` not being inherited in IE 8/9. + * 2. Remove padding so people aren't caught out if they zero out fieldsets. + */ +legend { + padding: 0; + /* 2 */ + border: 0; + /* 1 */ } + +/** + * 1. Correct font family not being inherited in all browsers. + * 2. Correct font size not being inherited in all browsers. + * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. + * 4. Remove any default :focus styles + * 5. Make sure webkit font smoothing is being inherited + * 6. Remove default gradient in Android Firefox / FirefoxOS + */ +button, +input, +select, +textarea { + margin: 0; + /* 3 */ + font-size: 100%; + /* 2 */ + font-family: inherit; + /* 1 */ + outline-offset: 0; + /* 4 */ + outline-style: none; + /* 4 */ + outline-width: 0; + /* 4 */ + -webkit-font-smoothing: inherit; + /* 5 */ + background-image: none; + /* 6 */ } + +/** + * Address Firefox 4+ setting `line-height` on `input` using `importnt` in + * the UA stylesheet. + */ +button, +input { + line-height: normal; } + +/** + * Address inconsistent `text-transform` inheritance for `button` and `select`. + * All other form control elements do not inherit `text-transform` values. + * Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. + * Correct `select` style inheritance in Firefox 4+ and Opera. + */ +button, +select { + text-transform: none; } + +/** + * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` + * and `video` controls. + * 2. Correct inability to style clickable `input` types in iOS. + * 3. Improve usability and consistency of cursor style between image-type + * `input` and others. + */ +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + cursor: pointer; + /* 3 */ + -webkit-appearance: button; + /* 2 */ } + +/** + * Re-set default cursor for disabled elements. + */ +button[disabled], +html input[disabled] { + cursor: default; } + +/** + * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. + * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome + * (include `-moz` to future-proof). + */ +input[type="search"] { + -webkit-box-sizing: content-box; + /* 2 */ + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; + /* 1 */ } + +/** + * Remove inner padding and search cancel button in Safari 5 and Chrome + * on OS X. + */ +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; } + +/** + * Remove inner padding and border in Firefox 4+. + */ +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; } + +/** + * 1. Remove default vertical scrollbar in IE 8/9. + * 2. Improve readability and alignment in all browsers. + */ +textarea { + overflow: auto; + /* 1 */ + vertical-align: top; + /* 2 */ } + +img { + -webkit-user-drag: none; } + +/* ========================================================================== + Tables + ========================================================================== */ +/** + * Remove most spacing between table cells. + */ +table { + border-spacing: 0; + border-collapse: collapse; } + +/** + * Scaffolding + * -------------------------------------------------- + */ +*, +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; } + +html { + overflow: hidden; + -ms-touch-action: pan-y; + touch-action: pan-y; } + +body, +.ionic-body { + -webkit-touch-callout: none; + -webkit-font-smoothing: antialiased; + font-smoothing: antialiased; + -webkit-text-size-adjust: none; + -moz-text-size-adjust: none; + text-size-adjust: none; + -webkit-tap-highlight-color: transparent; + -webkit-tap-highlight-color: transparent; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + top: 0; + right: 0; + bottom: 0; + left: 0; + overflow: hidden; + margin: 0; + padding: 0; + color: #000; + word-wrap: break-word; + font-size: 14px; + font-family: -apple-system; + font-family: "-apple-system", "Helvetica Neue", "Roboto", "Segoe UI", sans-serif; + line-height: 20px; + text-rendering: optimizeLegibility; + -webkit-backface-visibility: hidden; + -webkit-user-drag: none; + -ms-content-zooming: none; } + +body.grade-b, +body.grade-c { + text-rendering: auto; } + +.content { + position: relative; } + +.scroll-content { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + overflow: hidden; + margin-top: -1px; + padding-top: 1px; + margin-bottom: -1px; + width: auto; + height: auto; } + +.menu .scroll-content.scroll-content-false { + z-index: 11; } + +.scroll-view { + position: relative; + display: block; + overflow: hidden; + margin-top: -1px; } + .scroll-view.overflow-scroll { + position: relative; } + .scroll-view.scroll-x { + overflow-x: scroll; + overflow-y: hidden; } + .scroll-view.scroll-y { + overflow-x: hidden; + overflow-y: scroll; } + .scroll-view.scroll-xy { + overflow-x: scroll; + overflow-y: scroll; } + +/** + * Scroll is the scroll view component available for complex and custom + * scroll view functionality. + */ +.scroll { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-touch-callout: none; + -webkit-text-size-adjust: none; + -moz-text-size-adjust: none; + text-size-adjust: none; + -webkit-transform-origin: left top; + transform-origin: left top; } + +/** + * Set ms-viewport to prevent MS "page squish" and allow fluid scrolling + * https://msdn.microsoft.com/en-us/library/ie/hh869615(v=vs.85).aspx + */ +@-ms-viewport { + width: device-width; } + +.scroll-bar { + position: absolute; + z-index: 9999; } + +.ng-animate .scroll-bar { + visibility: hidden; } + +.scroll-bar-h { + right: 2px; + bottom: 3px; + left: 2px; + height: 3px; } + .scroll-bar-h .scroll-bar-indicator { + height: 100%; } + +.scroll-bar-v { + top: 2px; + right: 3px; + bottom: 2px; + width: 3px; } + .scroll-bar-v .scroll-bar-indicator { + width: 100%; } + +.scroll-bar-indicator { + position: absolute; + border-radius: 4px; + background: rgba(0, 0, 0, 0.3); + opacity: 1; + -webkit-transition: opacity 0.3s linear; + transition: opacity 0.3s linear; } + .scroll-bar-indicator.scroll-bar-fade-out { + opacity: 0; } + +.platform-android .scroll-bar-indicator { + border-radius: 0; } + +.grade-b .scroll-bar-indicator, +.grade-c .scroll-bar-indicator { + background: #aaa; } + .grade-b .scroll-bar-indicator.scroll-bar-fade-out, + .grade-c .scroll-bar-indicator.scroll-bar-fade-out { + -webkit-transition: none; + transition: none; } + +ion-infinite-scroll { + height: 60px; + width: 100%; + display: block; + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-direction: normal; + -webkit-box-orient: horizontal; + -webkit-flex-direction: row; + -moz-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + -moz-justify-content: center; + justify-content: center; + -webkit-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + -moz-align-items: center; + align-items: center; } + ion-infinite-scroll .icon { + color: #666666; + font-size: 30px; + color: #666666; } + ion-infinite-scroll:not(.active) .spinner, + ion-infinite-scroll:not(.active) .icon:before { + display: none; } + +.overflow-scroll { + overflow-x: hidden; + overflow-y: scroll; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; + top: 0; + right: 0; + bottom: 0; + left: 0; + position: absolute; } + .overflow-scroll.pane { + overflow-x: hidden; + overflow-y: scroll; } + .overflow-scroll .scroll { + position: static; + height: 100%; + -webkit-transform: translate3d(0, 0, 0); } + +/* If you change these, change platform.scss as well */ +.has-header { + top: 44px; } + +.no-header { + top: 0; } + +.has-subheader { + top: 88px; } + +.has-tabs-top { + top: 93px; } + +.has-header.has-subheader.has-tabs-top { + top: 137px; } + +.has-footer { + bottom: 44px; } + +.has-subfooter { + bottom: 88px; } + +.has-tabs, +.bar-footer.has-tabs { + bottom: 49px; } + .has-tabs.pane, + .bar-footer.has-tabs.pane { + bottom: 49px; + height: auto; } + +.bar-subfooter.has-tabs { + bottom: 93px; } + +.has-footer.has-tabs { + bottom: 93px; } + +.pane { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + -webkit-transition-duration: 0; + transition-duration: 0; + z-index: 1; } + +.view { + z-index: 1; } + +.pane, +.view { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + background-color: #fff; + overflow: hidden; } + +.view-container { + position: absolute; + display: block; + width: 100%; + height: 100%; } + +/** + * Typography + * -------------------------------------------------- + */ +p { + margin: 0 0 10px; } + +small { + font-size: 85%; } + +cite { + font-style: normal; } + +.text-left { + text-align: left; } + +.text-right { + text-align: right; } + +.text-center { + text-align: center; } + +h1, h2, h3, h4, h5, h6, +.h1, .h2, .h3, .h4, .h5, .h6 { + color: #000; + font-weight: 500; + font-family: "-apple-system", "Helvetica Neue", "Roboto", "Segoe UI", sans-serif; + line-height: 1.2; } + h1 small, h2 small, h3 small, h4 small, h5 small, h6 small, + .h1 small, .h2 small, .h3 small, .h4 small, .h5 small, .h6 small { + font-weight: normal; + line-height: 1; } + +h1, .h1, +h2, .h2, +h3, .h3 { + margin-top: 20px; + margin-bottom: 10px; } + h1:first-child, .h1:first-child, + h2:first-child, .h2:first-child, + h3:first-child, .h3:first-child { + margin-top: 0; } + h1 + h1, h1 + .h1, + h1 + h2, h1 + .h2, + h1 + h3, h1 + .h3, .h1 + h1, .h1 + .h1, + .h1 + h2, .h1 + .h2, + .h1 + h3, .h1 + .h3, + h2 + h1, + h2 + .h1, + h2 + h2, + h2 + .h2, + h2 + h3, + h2 + .h3, .h2 + h1, .h2 + .h1, + .h2 + h2, .h2 + .h2, + .h2 + h3, .h2 + .h3, + h3 + h1, + h3 + .h1, + h3 + h2, + h3 + .h2, + h3 + h3, + h3 + .h3, .h3 + h1, .h3 + .h1, + .h3 + h2, .h3 + .h2, + .h3 + h3, .h3 + .h3 { + margin-top: 10px; } + +h4, .h4, +h5, .h5, +h6, .h6 { + margin-top: 10px; + margin-bottom: 10px; } + +h1, .h1 { + font-size: 36px; } + +h2, .h2 { + font-size: 30px; } + +h3, .h3 { + font-size: 24px; } + +h4, .h4 { + font-size: 18px; } + +h5, .h5 { + font-size: 14px; } + +h6, .h6 { + font-size: 12px; } + +h1 small, .h1 small { + font-size: 24px; } + +h2 small, .h2 small { + font-size: 18px; } + +h3 small, .h3 small, +h4 small, .h4 small { + font-size: 14px; } + +dl { + margin-bottom: 20px; } + +dt, +dd { + line-height: 1.42857; } + +dt { + font-weight: bold; } + +blockquote { + margin: 0 0 20px; + padding: 10px 20px; + border-left: 5px solid gray; } + blockquote p { + font-weight: 300; + font-size: 17.5px; + line-height: 1.25; } + blockquote p:last-child { + margin-bottom: 0; } + blockquote small { + display: block; + line-height: 1.42857; } + blockquote small:before { + content: '\2014 \00A0'; } + +q:before, +q:after, +blockquote:before, +blockquote:after { + content: ""; } + +address { + display: block; + margin-bottom: 20px; + font-style: normal; + line-height: 1.42857; } + +a { + color: #387ef5; } + +a.subdued { + padding-right: 10px; + color: #888; + text-decoration: none; } + a.subdued:hover { + text-decoration: none; } + a.subdued:last-child { + padding-right: 0; } + +/** + * Action Sheets + * -------------------------------------------------- + */ +.action-sheet-backdrop { + -webkit-transition: background-color 150ms ease-in-out; + transition: background-color 150ms ease-in-out; + position: fixed; + top: 0; + left: 0; + z-index: 11; + width: 100%; + height: 100%; + background-color: transparent; } + .action-sheet-backdrop.active { + background-color: rgba(0, 0, 0, 0.4); } + +.action-sheet-wrapper { + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + -webkit-transition: all cubic-bezier(0.36, 0.66, 0.04, 1) 500ms; + transition: all cubic-bezier(0.36, 0.66, 0.04, 1) 500ms; + position: absolute; + bottom: 0; + left: 0; + right: 0; + width: 100%; + max-width: 500px; + margin: auto; } + +.action-sheet-up { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); } + +.action-sheet { + margin-left: 8px; + margin-right: 8px; + width: auto; + z-index: 11; + overflow: hidden; } + .action-sheet .button { + display: block; + padding: 1px; + width: 100%; + border-radius: 0; + border-color: #d1d3d6; + background-color: transparent; + color: #007aff; + font-size: 21px; } + .action-sheet .button:hover { + color: #007aff; } + .action-sheet .button.destructive { + color: #ff3b30; } + .action-sheet .button.destructive:hover { + color: #ff3b30; } + .action-sheet .button.active, .action-sheet .button.activated { + box-shadow: none; + border-color: #d1d3d6; + color: #007aff; + background: #e4e5e7; } + +.action-sheet-has-icons .icon { + position: absolute; + left: 16px; } + +.action-sheet-title { + padding: 16px; + color: #8f8f8f; + text-align: center; + font-size: 13px; } + +.action-sheet-group { + margin-bottom: 8px; + border-radius: 4px; + background-color: #fff; + overflow: hidden; } + .action-sheet-group .button { + border-width: 1px 0px 0px 0px; } + .action-sheet-group .button:first-child:last-child { + border-width: 0; } + +.action-sheet-options { + background: #f1f2f3; } + +.action-sheet-cancel .button { + font-weight: 500; } + +.action-sheet-open { + pointer-events: none; } + .action-sheet-open.modal-open .modal { + pointer-events: none; } + .action-sheet-open .action-sheet-backdrop { + pointer-events: auto; } + +.platform-android .action-sheet-backdrop.active { + background-color: rgba(0, 0, 0, 0.2); } + +.platform-android .action-sheet { + margin: 0; } + .platform-android .action-sheet .action-sheet-title, + .platform-android .action-sheet .button { + text-align: left; + border-color: transparent; + font-size: 16px; + color: inherit; } + .platform-android .action-sheet .action-sheet-title { + font-size: 14px; + padding: 16px; + color: #666; } + .platform-android .action-sheet .button.active, + .platform-android .action-sheet .button.activated { + background: #e8e8e8; } + +.platform-android .action-sheet-group { + margin: 0; + border-radius: 0; + background-color: #fafafa; } + +.platform-android .action-sheet-cancel { + display: none; } + +.platform-android .action-sheet-has-icons .button { + padding-left: 56px; } + +.backdrop { + position: fixed; + top: 0; + left: 0; + z-index: 11; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.4); + visibility: hidden; + opacity: 0; + -webkit-transition: 0.1s opacity linear; + transition: 0.1s opacity linear; } + .backdrop.visible { + visibility: visible; } + .backdrop.active { + opacity: 1; } + +/** + * Bar (Headers and Footers) + * -------------------------------------------------- + */ +.bar { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + position: absolute; + right: 0; + left: 0; + z-index: 9; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 5px; + width: 100%; + height: 44px; + border-width: 0; + border-style: solid; + border-top: 1px solid transparent; + border-bottom: 1px solid #ddd; + background-color: white; + /* border-width: 1px will actually create 2 device pixels on retina */ + /* this nifty trick sets an actual 1px border on hi-res displays */ + background-size: 0; } + @media (min--moz-device-pixel-ratio: 1.5), (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5), (min-resolution: 144dpi), (min-resolution: 1.5dppx) { + .bar { + border: none; + background-image: linear-gradient(0deg, #ddd, #ddd 50%, transparent 50%); + background-position: bottom; + background-size: 100% 1px; + background-repeat: no-repeat; } } + .bar.bar-clear { + border: none; + background: none; + color: #fff; } + .bar.bar-clear .button { + color: #fff; } + .bar.bar-clear .title { + color: #fff; } + .bar.item-input-inset .item-input-wrapper { + margin-top: -1px; } + .bar.item-input-inset .item-input-wrapper input { + padding-left: 8px; + width: 94%; + height: 28px; + background: transparent; } + .bar.bar-light { + border-color: #ddd; + background-color: white; + background-image: linear-gradient(0deg, #ddd, #ddd 50%, transparent 50%); + color: #444; } + .bar.bar-light .title { + color: #444; } + .bar.bar-light.bar-footer { + background-image: linear-gradient(180deg, #ddd, #ddd 50%, transparent 50%); } + .bar.bar-stable { + border-color: #b2b2b2; + background-color: #f8f8f8; + background-image: linear-gradient(0deg, #b2b2b2, #b2b2b2 50%, transparent 50%); + color: #444; } + .bar.bar-stable .title { + color: #444; } + .bar.bar-stable.bar-footer { + background-image: linear-gradient(180deg, #b2b2b2, #b2b2b2 50%, transparent 50%); } + .bar.bar-positive { + border-color: #0c60ee; + background-color: #387ef5; + background-image: linear-gradient(0deg, #0c60ee, #0c60ee 50%, transparent 50%); + color: #fff; } + .bar.bar-positive .title { + color: #fff; } + .bar.bar-positive.bar-footer { + background-image: linear-gradient(180deg, #0c60ee, #0c60ee 50%, transparent 50%); } + .bar.bar-calm { + border-color: #0a9dc7; + background-color: #11c1f3; + background-image: linear-gradient(0deg, #0a9dc7, #0a9dc7 50%, transparent 50%); + color: #fff; } + .bar.bar-calm .title { + color: #fff; } + .bar.bar-calm.bar-footer { + background-image: linear-gradient(180deg, #0a9dc7, #0a9dc7 50%, transparent 50%); } + .bar.bar-assertive { + border-color: #e42112; + background-color: #ef473a; + background-image: linear-gradient(0deg, #e42112, #e42112 50%, transparent 50%); + color: #fff; } + .bar.bar-assertive .title { + color: #fff; } + .bar.bar-assertive.bar-footer { + background-image: linear-gradient(180deg, #e42112, #e42112 50%, transparent 50%); } + .bar.bar-balanced { + border-color: #28a54c; + background-color: #33cd5f; + background-image: linear-gradient(0deg, #28a54c, #28a54c 50%, transparent 50%); + color: #fff; } + .bar.bar-balanced .title { + color: #fff; } + .bar.bar-balanced.bar-footer { + background-image: linear-gradient(180deg, #28a54c, #28a54c 50%, transparent 50%); } + .bar.bar-energized { + border-color: #e6b500; + background-color: #ffc900; + background-image: linear-gradient(0deg, #e6b500, #e6b500 50%, transparent 50%); + color: #fff; } + .bar.bar-energized .title { + color: #fff; } + .bar.bar-energized.bar-footer { + background-image: linear-gradient(180deg, #e6b500, #e6b500 50%, transparent 50%); } + .bar.bar-royal { + border-color: #6b46e5; + background-color: #886aea; + background-image: linear-gradient(0deg, #6b46e5, #6b46e5 50%, transparent 50%); + color: #fff; } + .bar.bar-royal .title { + color: #fff; } + .bar.bar-royal.bar-footer { + background-image: linear-gradient(180deg, #6b46e5, #6b46e5 50%, transparent 50%); } + .bar.bar-dark { + border-color: #111; + background-color: #444444; + background-image: linear-gradient(0deg, #111, #111 50%, transparent 50%); + color: #fff; } + .bar.bar-dark .title { + color: #fff; } + .bar.bar-dark.bar-footer { + background-image: linear-gradient(180deg, #111, #111 50%, transparent 50%); } + .bar .title { + display: block; + position: absolute; + top: 0; + right: 0; + left: 0; + z-index: 0; + overflow: hidden; + margin: 0 10px; + min-width: 30px; + height: 43px; + text-align: center; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 17px; + font-weight: 500; + line-height: 44px; } + .bar .title.title-left { + text-align: left; } + .bar .title.title-right { + text-align: right; } + .bar .title a { + color: inherit; } + .bar .button, .bar button { + z-index: 1; + padding: 0 8px; + min-width: initial; + min-height: 31px; + font-weight: 400; + font-size: 13px; + line-height: 32px; } + .bar .button.button-icon:before, + .bar .button .icon:before, .bar .button.icon:before, .bar .button.icon-left:before, .bar .button.icon-right:before, .bar button.button-icon:before, + .bar button .icon:before, .bar button.icon:before, .bar button.icon-left:before, .bar button.icon-right:before { + padding-right: 2px; + padding-left: 2px; + font-size: 20px; + line-height: 32px; } + .bar .button.button-icon, .bar button.button-icon { + font-size: 17px; } + .bar .button.button-icon .icon:before, .bar .button.button-icon:before, .bar .button.button-icon.icon-left:before, .bar .button.button-icon.icon-right:before, .bar button.button-icon .icon:before, .bar button.button-icon:before, .bar button.button-icon.icon-left:before, .bar button.button-icon.icon-right:before { + vertical-align: top; + font-size: 32px; + line-height: 32px; } + .bar .button.button-clear, .bar button.button-clear { + padding-right: 2px; + padding-left: 2px; + font-weight: 300; + font-size: 17px; } + .bar .button.button-clear .icon:before, .bar .button.button-clear.icon:before, .bar .button.button-clear.icon-left:before, .bar .button.button-clear.icon-right:before, .bar button.button-clear .icon:before, .bar button.button-clear.icon:before, .bar button.button-clear.icon-left:before, .bar button.button-clear.icon-right:before { + font-size: 32px; + line-height: 32px; } + .bar .button.back-button, .bar button.back-button { + display: block; + margin-right: 5px; + padding: 0; + white-space: nowrap; + font-weight: 400; } + .bar .button.back-button.active, .bar .button.back-button.activated, .bar button.back-button.active, .bar button.back-button.activated { + opacity: 0.2; } + .bar .button-bar > .button, + .bar .buttons > .button { + min-height: 31px; + line-height: 32px; } + .bar .button-bar + .button, + .bar .button + .button-bar { + margin-left: 5px; } + .bar .buttons, + .bar .buttons.primary-buttons, + .bar .buttons.secondary-buttons { + display: inherit; } + .bar .buttons span { + display: inline-block; } + .bar .buttons-left span { + margin-right: 5px; + display: inherit; } + .bar .buttons-right span { + margin-left: 5px; + display: inherit; } + .bar .title + .button:last-child, + .bar > .button + .button:last-child, + .bar > .button.pull-right, + .bar .buttons.pull-right, + .bar .title + .buttons { + position: absolute; + top: 5px; + right: 5px; + bottom: 5px; } + +.platform-android .nav-bar-has-subheader .bar { + background-image: none; } + +.platform-android .bar .back-button .icon:before { + font-size: 24px; } + +.platform-android .bar .title { + font-size: 19px; + line-height: 44px; } + +.bar-light .button { + border-color: #ddd; + background-color: white; + color: #444; } + .bar-light .button:hover { + color: #444; + text-decoration: none; } + .bar-light .button.active, .bar-light .button.activated { + border-color: #ccc; + background-color: #fafafa; } + .bar-light .button.button-clear { + border-color: transparent; + background: none; + box-shadow: none; + color: #444; + font-size: 17px; } + .bar-light .button.button-icon { + border-color: transparent; + background: none; } + +.bar-stable .button { + border-color: #b2b2b2; + background-color: #f8f8f8; + color: #444; } + .bar-stable .button:hover { + color: #444; + text-decoration: none; } + .bar-stable .button.active, .bar-stable .button.activated { + border-color: #a2a2a2; + background-color: #e5e5e5; } + .bar-stable .button.button-clear { + border-color: transparent; + background: none; + box-shadow: none; + color: #444; + font-size: 17px; } + .bar-stable .button.button-icon { + border-color: transparent; + background: none; } + +.bar-positive .button { + border-color: #0c60ee; + background-color: #387ef5; + color: #fff; } + .bar-positive .button:hover { + color: #fff; + text-decoration: none; } + .bar-positive .button.active, .bar-positive .button.activated { + border-color: #0c60ee; + background-color: #0c60ee; } + .bar-positive .button.button-clear { + border-color: transparent; + background: none; + box-shadow: none; + color: #fff; + font-size: 17px; } + .bar-positive .button.button-icon { + border-color: transparent; + background: none; } + +.bar-calm .button { + border-color: #0a9dc7; + background-color: #11c1f3; + color: #fff; } + .bar-calm .button:hover { + color: #fff; + text-decoration: none; } + .bar-calm .button.active, .bar-calm .button.activated { + border-color: #0a9dc7; + background-color: #0a9dc7; } + .bar-calm .button.button-clear { + border-color: transparent; + background: none; + box-shadow: none; + color: #fff; + font-size: 17px; } + .bar-calm .button.button-icon { + border-color: transparent; + background: none; } + +.bar-assertive .button { + border-color: #e42112; + background-color: #ef473a; + color: #fff; } + .bar-assertive .button:hover { + color: #fff; + text-decoration: none; } + .bar-assertive .button.active, .bar-assertive .button.activated { + border-color: #e42112; + background-color: #e42112; } + .bar-assertive .button.button-clear { + border-color: transparent; + background: none; + box-shadow: none; + color: #fff; + font-size: 17px; } + .bar-assertive .button.button-icon { + border-color: transparent; + background: none; } + +.bar-balanced .button { + border-color: #28a54c; + background-color: #33cd5f; + color: #fff; } + .bar-balanced .button:hover { + color: #fff; + text-decoration: none; } + .bar-balanced .button.active, .bar-balanced .button.activated { + border-color: #28a54c; + background-color: #28a54c; } + .bar-balanced .button.button-clear { + border-color: transparent; + background: none; + box-shadow: none; + color: #fff; + font-size: 17px; } + .bar-balanced .button.button-icon { + border-color: transparent; + background: none; } + +.bar-energized .button { + border-color: #e6b500; + background-color: #ffc900; + color: #fff; } + .bar-energized .button:hover { + color: #fff; + text-decoration: none; } + .bar-energized .button.active, .bar-energized .button.activated { + border-color: #e6b500; + background-color: #e6b500; } + .bar-energized .button.button-clear { + border-color: transparent; + background: none; + box-shadow: none; + color: #fff; + font-size: 17px; } + .bar-energized .button.button-icon { + border-color: transparent; + background: none; } + +.bar-royal .button { + border-color: #6b46e5; + background-color: #886aea; + color: #fff; } + .bar-royal .button:hover { + color: #fff; + text-decoration: none; } + .bar-royal .button.active, .bar-royal .button.activated { + border-color: #6b46e5; + background-color: #6b46e5; } + .bar-royal .button.button-clear { + border-color: transparent; + background: none; + box-shadow: none; + color: #fff; + font-size: 17px; } + .bar-royal .button.button-icon { + border-color: transparent; + background: none; } + +.bar-dark .button { + border-color: #111; + background-color: #444444; + color: #fff; } + .bar-dark .button:hover { + color: #fff; + text-decoration: none; } + .bar-dark .button.active, .bar-dark .button.activated { + border-color: #000; + background-color: #262626; } + .bar-dark .button.button-clear { + border-color: transparent; + background: none; + box-shadow: none; + color: #fff; + font-size: 17px; } + .bar-dark .button.button-icon { + border-color: transparent; + background: none; } + +.bar-header { + top: 0; + border-top-width: 0; + border-bottom-width: 1px; } + .bar-header.has-tabs-top { + border-bottom-width: 0px; + background-image: none; } + +.tabs-top .bar-header { + border-bottom-width: 0px; + background-image: none; } + +.bar-footer { + bottom: 0; + border-top-width: 1px; + border-bottom-width: 0; + background-position: top; + height: 44px; } + .bar-footer.item-input-inset { + position: absolute; } + .bar-footer .title { + height: 43px; + line-height: 44px; } + +.bar-tabs { + padding: 0; } + +.bar-subheader { + top: 44px; + height: 44px; } + .bar-subheader .title { + height: 43px; + line-height: 44px; } + +.bar-subfooter { + bottom: 44px; + height: 44px; } + .bar-subfooter .title { + height: 43px; + line-height: 44px; } + +.nav-bar-block { + position: absolute; + top: 0; + right: 0; + left: 0; + z-index: 9; } + +.bar .back-button.hide, +.bar .buttons .hide { + display: none; } + +.nav-bar-tabs-top .bar { + background-image: none; } + +/** + * Tabs + * -------------------------------------------------- + * A navigation bar with any number of tab items supported. + */ +.tabs { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-direction: normal; + -webkit-box-orient: horizontal; + -webkit-flex-direction: horizontal; + -moz-flex-direction: horizontal; + -ms-flex-direction: horizontal; + flex-direction: horizontal; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + -moz-justify-content: center; + justify-content: center; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + border-color: #b2b2b2; + background-color: #f8f8f8; + background-image: linear-gradient(0deg, #b2b2b2, #b2b2b2 50%, transparent 50%); + color: #444; + position: absolute; + bottom: 0; + z-index: 5; + width: 100%; + height: 49px; + border-style: solid; + border-top-width: 1px; + background-size: 0; + line-height: 49px; } + .tabs .tab-item .badge { + background-color: #444; + color: #f8f8f8; } + @media (min--moz-device-pixel-ratio: 1.5), (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5), (min-resolution: 144dpi), (min-resolution: 1.5dppx) { + .tabs { + padding-top: 2px; + border-top: none !important; + border-bottom: none; + background-position: top; + background-size: 100% 1px; + background-repeat: no-repeat; } } + +/* Allow parent element of tabs to define color, or just the tab itself */ +.tabs-light > .tabs, +.tabs.tabs-light { + border-color: #ddd; + background-color: #fff; + background-image: linear-gradient(0deg, #ddd, #ddd 50%, transparent 50%); + color: #444; } + .tabs-light > .tabs .tab-item .badge, + .tabs.tabs-light .tab-item .badge { + background-color: #444; + color: #fff; } + +.tabs-stable > .tabs, +.tabs.tabs-stable { + border-color: #b2b2b2; + background-color: #f8f8f8; + background-image: linear-gradient(0deg, #b2b2b2, #b2b2b2 50%, transparent 50%); + color: #444; } + .tabs-stable > .tabs .tab-item .badge, + .tabs.tabs-stable .tab-item .badge { + background-color: #444; + color: #f8f8f8; } + +.tabs-positive > .tabs, +.tabs.tabs-positive { + border-color: #0c60ee; + background-color: #387ef5; + background-image: linear-gradient(0deg, #0c60ee, #0c60ee 50%, transparent 50%); + color: #fff; } + .tabs-positive > .tabs .tab-item .badge, + .tabs.tabs-positive .tab-item .badge { + background-color: #fff; + color: #387ef5; } + +.tabs-calm > .tabs, +.tabs.tabs-calm { + border-color: #0a9dc7; + background-color: #11c1f3; + background-image: linear-gradient(0deg, #0a9dc7, #0a9dc7 50%, transparent 50%); + color: #fff; } + .tabs-calm > .tabs .tab-item .badge, + .tabs.tabs-calm .tab-item .badge { + background-color: #fff; + color: #11c1f3; } + +.tabs-assertive > .tabs, +.tabs.tabs-assertive { + border-color: #e42112; + background-color: #ef473a; + background-image: linear-gradient(0deg, #e42112, #e42112 50%, transparent 50%); + color: #fff; } + .tabs-assertive > .tabs .tab-item .badge, + .tabs.tabs-assertive .tab-item .badge { + background-color: #fff; + color: #ef473a; } + +.tabs-balanced > .tabs, +.tabs.tabs-balanced { + border-color: #28a54c; + background-color: #33cd5f; + background-image: linear-gradient(0deg, #28a54c, #28a54c 50%, transparent 50%); + color: #fff; } + .tabs-balanced > .tabs .tab-item .badge, + .tabs.tabs-balanced .tab-item .badge { + background-color: #fff; + color: #33cd5f; } + +.tabs-energized > .tabs, +.tabs.tabs-energized { + border-color: #e6b500; + background-color: #ffc900; + background-image: linear-gradient(0deg, #e6b500, #e6b500 50%, transparent 50%); + color: #fff; } + .tabs-energized > .tabs .tab-item .badge, + .tabs.tabs-energized .tab-item .badge { + background-color: #fff; + color: #ffc900; } + +.tabs-royal > .tabs, +.tabs.tabs-royal { + border-color: #6b46e5; + background-color: #886aea; + background-image: linear-gradient(0deg, #6b46e5, #6b46e5 50%, transparent 50%); + color: #fff; } + .tabs-royal > .tabs .tab-item .badge, + .tabs.tabs-royal .tab-item .badge { + background-color: #fff; + color: #886aea; } + +.tabs-dark > .tabs, +.tabs.tabs-dark { + border-color: #111; + background-color: #444; + background-image: linear-gradient(0deg, #111, #111 50%, transparent 50%); + color: #fff; } + .tabs-dark > .tabs .tab-item .badge, + .tabs.tabs-dark .tab-item .badge { + background-color: #fff; + color: #444; } + +.tabs-striped .tabs { + background-color: white; + background-image: none; + border: none; + border-bottom: 1px solid #ddd; + padding-top: 2px; } + +.tabs-striped .tab-item.tab-item-active, .tabs-striped .tab-item.active, .tabs-striped .tab-item.activated { + margin-top: -2px; + border-style: solid; + border-width: 2px 0 0 0; + border-color: #444; } + .tabs-striped .tab-item.tab-item-active .badge, .tabs-striped .tab-item.active .badge, .tabs-striped .tab-item.activated .badge { + top: 2px; + opacity: 1; } + +.tabs-striped.tabs-light .tabs { + background-color: #fff; } + +.tabs-striped.tabs-light .tab-item { + color: rgba(68, 68, 68, 0.4); + opacity: 1; } + .tabs-striped.tabs-light .tab-item .badge { + opacity: 0.4; } + .tabs-striped.tabs-light .tab-item.tab-item-active, .tabs-striped.tabs-light .tab-item.active, .tabs-striped.tabs-light .tab-item.activated { + margin-top: -2px; + color: #444; + border-style: solid; + border-width: 2px 0 0 0; + border-color: #444; } + +.tabs-striped.tabs-top .tab-item.tab-item-active .badge, .tabs-striped.tabs-top .tab-item.active .badge, .tabs-striped.tabs-top .tab-item.activated .badge { + top: 4%; } + +.tabs-striped.tabs-stable .tabs { + background-color: #f8f8f8; } + +.tabs-striped.tabs-stable .tab-item { + color: rgba(68, 68, 68, 0.4); + opacity: 1; } + .tabs-striped.tabs-stable .tab-item .badge { + opacity: 0.4; } + .tabs-striped.tabs-stable .tab-item.tab-item-active, .tabs-striped.tabs-stable .tab-item.active, .tabs-striped.tabs-stable .tab-item.activated { + margin-top: -2px; + color: #444; + border-style: solid; + border-width: 2px 0 0 0; + border-color: #444; } + +.tabs-striped.tabs-top .tab-item.tab-item-active .badge, .tabs-striped.tabs-top .tab-item.active .badge, .tabs-striped.tabs-top .tab-item.activated .badge { + top: 4%; } + +.tabs-striped.tabs-positive .tabs { + background-color: #387ef5; } + +.tabs-striped.tabs-positive .tab-item { + color: rgba(255, 255, 255, 0.4); + opacity: 1; } + .tabs-striped.tabs-positive .tab-item .badge { + opacity: 0.4; } + .tabs-striped.tabs-positive .tab-item.tab-item-active, .tabs-striped.tabs-positive .tab-item.active, .tabs-striped.tabs-positive .tab-item.activated { + margin-top: -2px; + color: #fff; + border-style: solid; + border-width: 2px 0 0 0; + border-color: #fff; } + +.tabs-striped.tabs-top .tab-item.tab-item-active .badge, .tabs-striped.tabs-top .tab-item.active .badge, .tabs-striped.tabs-top .tab-item.activated .badge { + top: 4%; } + +.tabs-striped.tabs-calm .tabs { + background-color: #11c1f3; } + +.tabs-striped.tabs-calm .tab-item { + color: rgba(255, 255, 255, 0.4); + opacity: 1; } + .tabs-striped.tabs-calm .tab-item .badge { + opacity: 0.4; } + .tabs-striped.tabs-calm .tab-item.tab-item-active, .tabs-striped.tabs-calm .tab-item.active, .tabs-striped.tabs-calm .tab-item.activated { + margin-top: -2px; + color: #fff; + border-style: solid; + border-width: 2px 0 0 0; + border-color: #fff; } + +.tabs-striped.tabs-top .tab-item.tab-item-active .badge, .tabs-striped.tabs-top .tab-item.active .badge, .tabs-striped.tabs-top .tab-item.activated .badge { + top: 4%; } + +.tabs-striped.tabs-assertive .tabs { + background-color: #ef473a; } + +.tabs-striped.tabs-assertive .tab-item { + color: rgba(255, 255, 255, 0.4); + opacity: 1; } + .tabs-striped.tabs-assertive .tab-item .badge { + opacity: 0.4; } + .tabs-striped.tabs-assertive .tab-item.tab-item-active, .tabs-striped.tabs-assertive .tab-item.active, .tabs-striped.tabs-assertive .tab-item.activated { + margin-top: -2px; + color: #fff; + border-style: solid; + border-width: 2px 0 0 0; + border-color: #fff; } + +.tabs-striped.tabs-top .tab-item.tab-item-active .badge, .tabs-striped.tabs-top .tab-item.active .badge, .tabs-striped.tabs-top .tab-item.activated .badge { + top: 4%; } + +.tabs-striped.tabs-balanced .tabs { + background-color: #33cd5f; } + +.tabs-striped.tabs-balanced .tab-item { + color: rgba(255, 255, 255, 0.4); + opacity: 1; } + .tabs-striped.tabs-balanced .tab-item .badge { + opacity: 0.4; } + .tabs-striped.tabs-balanced .tab-item.tab-item-active, .tabs-striped.tabs-balanced .tab-item.active, .tabs-striped.tabs-balanced .tab-item.activated { + margin-top: -2px; + color: #fff; + border-style: solid; + border-width: 2px 0 0 0; + border-color: #fff; } + +.tabs-striped.tabs-top .tab-item.tab-item-active .badge, .tabs-striped.tabs-top .tab-item.active .badge, .tabs-striped.tabs-top .tab-item.activated .badge { + top: 4%; } + +.tabs-striped.tabs-energized .tabs { + background-color: #ffc900; } + +.tabs-striped.tabs-energized .tab-item { + color: rgba(255, 255, 255, 0.4); + opacity: 1; } + .tabs-striped.tabs-energized .tab-item .badge { + opacity: 0.4; } + .tabs-striped.tabs-energized .tab-item.tab-item-active, .tabs-striped.tabs-energized .tab-item.active, .tabs-striped.tabs-energized .tab-item.activated { + margin-top: -2px; + color: #fff; + border-style: solid; + border-width: 2px 0 0 0; + border-color: #fff; } + +.tabs-striped.tabs-top .tab-item.tab-item-active .badge, .tabs-striped.tabs-top .tab-item.active .badge, .tabs-striped.tabs-top .tab-item.activated .badge { + top: 4%; } + +.tabs-striped.tabs-royal .tabs { + background-color: #886aea; } + +.tabs-striped.tabs-royal .tab-item { + color: rgba(255, 255, 255, 0.4); + opacity: 1; } + .tabs-striped.tabs-royal .tab-item .badge { + opacity: 0.4; } + .tabs-striped.tabs-royal .tab-item.tab-item-active, .tabs-striped.tabs-royal .tab-item.active, .tabs-striped.tabs-royal .tab-item.activated { + margin-top: -2px; + color: #fff; + border-style: solid; + border-width: 2px 0 0 0; + border-color: #fff; } + +.tabs-striped.tabs-top .tab-item.tab-item-active .badge, .tabs-striped.tabs-top .tab-item.active .badge, .tabs-striped.tabs-top .tab-item.activated .badge { + top: 4%; } + +.tabs-striped.tabs-dark .tabs { + background-color: #444; } + +.tabs-striped.tabs-dark .tab-item { + color: rgba(255, 255, 255, 0.4); + opacity: 1; } + .tabs-striped.tabs-dark .tab-item .badge { + opacity: 0.4; } + .tabs-striped.tabs-dark .tab-item.tab-item-active, .tabs-striped.tabs-dark .tab-item.active, .tabs-striped.tabs-dark .tab-item.activated { + margin-top: -2px; + color: #fff; + border-style: solid; + border-width: 2px 0 0 0; + border-color: #fff; } + +.tabs-striped.tabs-top .tab-item.tab-item-active .badge, .tabs-striped.tabs-top .tab-item.active .badge, .tabs-striped.tabs-top .tab-item.activated .badge { + top: 4%; } + +.tabs-striped.tabs-background-light .tabs { + background-color: #fff; + background-image: none; } + +.tabs-striped.tabs-background-stable .tabs { + background-color: #f8f8f8; + background-image: none; } + +.tabs-striped.tabs-background-positive .tabs { + background-color: #387ef5; + background-image: none; } + +.tabs-striped.tabs-background-calm .tabs { + background-color: #11c1f3; + background-image: none; } + +.tabs-striped.tabs-background-assertive .tabs { + background-color: #ef473a; + background-image: none; } + +.tabs-striped.tabs-background-balanced .tabs { + background-color: #33cd5f; + background-image: none; } + +.tabs-striped.tabs-background-energized .tabs { + background-color: #ffc900; + background-image: none; } + +.tabs-striped.tabs-background-royal .tabs { + background-color: #886aea; + background-image: none; } + +.tabs-striped.tabs-background-dark .tabs { + background-color: #444; + background-image: none; } + +.tabs-striped.tabs-color-light .tab-item { + color: rgba(255, 255, 255, 0.4); + opacity: 1; } + .tabs-striped.tabs-color-light .tab-item .badge { + opacity: 0.4; } + .tabs-striped.tabs-color-light .tab-item.tab-item-active, .tabs-striped.tabs-color-light .tab-item.active, .tabs-striped.tabs-color-light .tab-item.activated { + margin-top: -2px; + color: #fff; + border: 0 solid #fff; + border-top-width: 2px; } + .tabs-striped.tabs-color-light .tab-item.tab-item-active .badge, .tabs-striped.tabs-color-light .tab-item.active .badge, .tabs-striped.tabs-color-light .tab-item.activated .badge { + top: 2px; + opacity: 1; } + +.tabs-striped.tabs-color-stable .tab-item { + color: rgba(248, 248, 248, 0.4); + opacity: 1; } + .tabs-striped.tabs-color-stable .tab-item .badge { + opacity: 0.4; } + .tabs-striped.tabs-color-stable .tab-item.tab-item-active, .tabs-striped.tabs-color-stable .tab-item.active, .tabs-striped.tabs-color-stable .tab-item.activated { + margin-top: -2px; + color: #f8f8f8; + border: 0 solid #f8f8f8; + border-top-width: 2px; } + .tabs-striped.tabs-color-stable .tab-item.tab-item-active .badge, .tabs-striped.tabs-color-stable .tab-item.active .badge, .tabs-striped.tabs-color-stable .tab-item.activated .badge { + top: 2px; + opacity: 1; } + +.tabs-striped.tabs-color-positive .tab-item { + color: rgba(56, 126, 245, 0.4); + opacity: 1; } + .tabs-striped.tabs-color-positive .tab-item .badge { + opacity: 0.4; } + .tabs-striped.tabs-color-positive .tab-item.tab-item-active, .tabs-striped.tabs-color-positive .tab-item.active, .tabs-striped.tabs-color-positive .tab-item.activated { + margin-top: -2px; + color: #387ef5; + border: 0 solid #387ef5; + border-top-width: 2px; } + .tabs-striped.tabs-color-positive .tab-item.tab-item-active .badge, .tabs-striped.tabs-color-positive .tab-item.active .badge, .tabs-striped.tabs-color-positive .tab-item.activated .badge { + top: 2px; + opacity: 1; } + +.tabs-striped.tabs-color-calm .tab-item { + color: rgba(17, 193, 243, 0.4); + opacity: 1; } + .tabs-striped.tabs-color-calm .tab-item .badge { + opacity: 0.4; } + .tabs-striped.tabs-color-calm .tab-item.tab-item-active, .tabs-striped.tabs-color-calm .tab-item.active, .tabs-striped.tabs-color-calm .tab-item.activated { + margin-top: -2px; + color: #11c1f3; + border: 0 solid #11c1f3; + border-top-width: 2px; } + .tabs-striped.tabs-color-calm .tab-item.tab-item-active .badge, .tabs-striped.tabs-color-calm .tab-item.active .badge, .tabs-striped.tabs-color-calm .tab-item.activated .badge { + top: 2px; + opacity: 1; } + +.tabs-striped.tabs-color-assertive .tab-item { + color: rgba(239, 71, 58, 0.4); + opacity: 1; } + .tabs-striped.tabs-color-assertive .tab-item .badge { + opacity: 0.4; } + .tabs-striped.tabs-color-assertive .tab-item.tab-item-active, .tabs-striped.tabs-color-assertive .tab-item.active, .tabs-striped.tabs-color-assertive .tab-item.activated { + margin-top: -2px; + color: #ef473a; + border: 0 solid #ef473a; + border-top-width: 2px; } + .tabs-striped.tabs-color-assertive .tab-item.tab-item-active .badge, .tabs-striped.tabs-color-assertive .tab-item.active .badge, .tabs-striped.tabs-color-assertive .tab-item.activated .badge { + top: 2px; + opacity: 1; } + +.tabs-striped.tabs-color-balanced .tab-item { + color: rgba(51, 205, 95, 0.4); + opacity: 1; } + .tabs-striped.tabs-color-balanced .tab-item .badge { + opacity: 0.4; } + .tabs-striped.tabs-color-balanced .tab-item.tab-item-active, .tabs-striped.tabs-color-balanced .tab-item.active, .tabs-striped.tabs-color-balanced .tab-item.activated { + margin-top: -2px; + color: #33cd5f; + border: 0 solid #33cd5f; + border-top-width: 2px; } + .tabs-striped.tabs-color-balanced .tab-item.tab-item-active .badge, .tabs-striped.tabs-color-balanced .tab-item.active .badge, .tabs-striped.tabs-color-balanced .tab-item.activated .badge { + top: 2px; + opacity: 1; } + +.tabs-striped.tabs-color-energized .tab-item { + color: rgba(255, 201, 0, 0.4); + opacity: 1; } + .tabs-striped.tabs-color-energized .tab-item .badge { + opacity: 0.4; } + .tabs-striped.tabs-color-energized .tab-item.tab-item-active, .tabs-striped.tabs-color-energized .tab-item.active, .tabs-striped.tabs-color-energized .tab-item.activated { + margin-top: -2px; + color: #ffc900; + border: 0 solid #ffc900; + border-top-width: 2px; } + .tabs-striped.tabs-color-energized .tab-item.tab-item-active .badge, .tabs-striped.tabs-color-energized .tab-item.active .badge, .tabs-striped.tabs-color-energized .tab-item.activated .badge { + top: 2px; + opacity: 1; } + +.tabs-striped.tabs-color-royal .tab-item { + color: rgba(136, 106, 234, 0.4); + opacity: 1; } + .tabs-striped.tabs-color-royal .tab-item .badge { + opacity: 0.4; } + .tabs-striped.tabs-color-royal .tab-item.tab-item-active, .tabs-striped.tabs-color-royal .tab-item.active, .tabs-striped.tabs-color-royal .tab-item.activated { + margin-top: -2px; + color: #886aea; + border: 0 solid #886aea; + border-top-width: 2px; } + .tabs-striped.tabs-color-royal .tab-item.tab-item-active .badge, .tabs-striped.tabs-color-royal .tab-item.active .badge, .tabs-striped.tabs-color-royal .tab-item.activated .badge { + top: 2px; + opacity: 1; } + +.tabs-striped.tabs-color-dark .tab-item { + color: rgba(68, 68, 68, 0.4); + opacity: 1; } + .tabs-striped.tabs-color-dark .tab-item .badge { + opacity: 0.4; } + .tabs-striped.tabs-color-dark .tab-item.tab-item-active, .tabs-striped.tabs-color-dark .tab-item.active, .tabs-striped.tabs-color-dark .tab-item.activated { + margin-top: -2px; + color: #444; + border: 0 solid #444; + border-top-width: 2px; } + .tabs-striped.tabs-color-dark .tab-item.tab-item-active .badge, .tabs-striped.tabs-color-dark .tab-item.active .badge, .tabs-striped.tabs-color-dark .tab-item.activated .badge { + top: 2px; + opacity: 1; } + +.tabs-background-light .tabs, +.tabs-background-light > .tabs { + background-color: #fff; + background-image: linear-gradient(0deg, #ddd, #ddd 50%, transparent 50%); + border-color: #ddd; } + +.tabs-background-stable .tabs, +.tabs-background-stable > .tabs { + background-color: #f8f8f8; + background-image: linear-gradient(0deg, #b2b2b2, #b2b2b2 50%, transparent 50%); + border-color: #b2b2b2; } + +.tabs-background-positive .tabs, +.tabs-background-positive > .tabs { + background-color: #387ef5; + background-image: linear-gradient(0deg, #0c60ee, #0c60ee 50%, transparent 50%); + border-color: #0c60ee; } + +.tabs-background-calm .tabs, +.tabs-background-calm > .tabs { + background-color: #11c1f3; + background-image: linear-gradient(0deg, #0a9dc7, #0a9dc7 50%, transparent 50%); + border-color: #0a9dc7; } + +.tabs-background-assertive .tabs, +.tabs-background-assertive > .tabs { + background-color: #ef473a; + background-image: linear-gradient(0deg, #e42112, #e42112 50%, transparent 50%); + border-color: #e42112; } + +.tabs-background-balanced .tabs, +.tabs-background-balanced > .tabs { + background-color: #33cd5f; + background-image: linear-gradient(0deg, #28a54c, #28a54c 50%, transparent 50%); + border-color: #28a54c; } + +.tabs-background-energized .tabs, +.tabs-background-energized > .tabs { + background-color: #ffc900; + background-image: linear-gradient(0deg, #e6b500, #e6b500 50%, transparent 50%); + border-color: #e6b500; } + +.tabs-background-royal .tabs, +.tabs-background-royal > .tabs { + background-color: #886aea; + background-image: linear-gradient(0deg, #6b46e5, #6b46e5 50%, transparent 50%); + border-color: #6b46e5; } + +.tabs-background-dark .tabs, +.tabs-background-dark > .tabs { + background-color: #444; + background-image: linear-gradient(0deg, #111, #111 50%, transparent 50%); + border-color: #111; } + +.tabs-color-light .tab-item { + color: rgba(255, 255, 255, 0.4); + opacity: 1; } + .tabs-color-light .tab-item .badge { + opacity: 0.4; } + .tabs-color-light .tab-item.tab-item-active, .tabs-color-light .tab-item.active, .tabs-color-light .tab-item.activated { + color: #fff; + border: 0 solid #fff; } + .tabs-color-light .tab-item.tab-item-active .badge, .tabs-color-light .tab-item.active .badge, .tabs-color-light .tab-item.activated .badge { + opacity: 1; } + +.tabs-color-stable .tab-item { + color: rgba(248, 248, 248, 0.4); + opacity: 1; } + .tabs-color-stable .tab-item .badge { + opacity: 0.4; } + .tabs-color-stable .tab-item.tab-item-active, .tabs-color-stable .tab-item.active, .tabs-color-stable .tab-item.activated { + color: #f8f8f8; + border: 0 solid #f8f8f8; } + .tabs-color-stable .tab-item.tab-item-active .badge, .tabs-color-stable .tab-item.active .badge, .tabs-color-stable .tab-item.activated .badge { + opacity: 1; } + +.tabs-color-positive .tab-item { + color: rgba(56, 126, 245, 0.4); + opacity: 1; } + .tabs-color-positive .tab-item .badge { + opacity: 0.4; } + .tabs-color-positive .tab-item.tab-item-active, .tabs-color-positive .tab-item.active, .tabs-color-positive .tab-item.activated { + color: #387ef5; + border: 0 solid #387ef5; } + .tabs-color-positive .tab-item.tab-item-active .badge, .tabs-color-positive .tab-item.active .badge, .tabs-color-positive .tab-item.activated .badge { + opacity: 1; } + +.tabs-color-calm .tab-item { + color: rgba(17, 193, 243, 0.4); + opacity: 1; } + .tabs-color-calm .tab-item .badge { + opacity: 0.4; } + .tabs-color-calm .tab-item.tab-item-active, .tabs-color-calm .tab-item.active, .tabs-color-calm .tab-item.activated { + color: #11c1f3; + border: 0 solid #11c1f3; } + .tabs-color-calm .tab-item.tab-item-active .badge, .tabs-color-calm .tab-item.active .badge, .tabs-color-calm .tab-item.activated .badge { + opacity: 1; } + +.tabs-color-assertive .tab-item { + color: rgba(239, 71, 58, 0.4); + opacity: 1; } + .tabs-color-assertive .tab-item .badge { + opacity: 0.4; } + .tabs-color-assertive .tab-item.tab-item-active, .tabs-color-assertive .tab-item.active, .tabs-color-assertive .tab-item.activated { + color: #ef473a; + border: 0 solid #ef473a; } + .tabs-color-assertive .tab-item.tab-item-active .badge, .tabs-color-assertive .tab-item.active .badge, .tabs-color-assertive .tab-item.activated .badge { + opacity: 1; } + +.tabs-color-balanced .tab-item { + color: rgba(51, 205, 95, 0.4); + opacity: 1; } + .tabs-color-balanced .tab-item .badge { + opacity: 0.4; } + .tabs-color-balanced .tab-item.tab-item-active, .tabs-color-balanced .tab-item.active, .tabs-color-balanced .tab-item.activated { + color: #33cd5f; + border: 0 solid #33cd5f; } + .tabs-color-balanced .tab-item.tab-item-active .badge, .tabs-color-balanced .tab-item.active .badge, .tabs-color-balanced .tab-item.activated .badge { + opacity: 1; } + +.tabs-color-energized .tab-item { + color: rgba(255, 201, 0, 0.4); + opacity: 1; } + .tabs-color-energized .tab-item .badge { + opacity: 0.4; } + .tabs-color-energized .tab-item.tab-item-active, .tabs-color-energized .tab-item.active, .tabs-color-energized .tab-item.activated { + color: #ffc900; + border: 0 solid #ffc900; } + .tabs-color-energized .tab-item.tab-item-active .badge, .tabs-color-energized .tab-item.active .badge, .tabs-color-energized .tab-item.activated .badge { + opacity: 1; } + +.tabs-color-royal .tab-item { + color: rgba(136, 106, 234, 0.4); + opacity: 1; } + .tabs-color-royal .tab-item .badge { + opacity: 0.4; } + .tabs-color-royal .tab-item.tab-item-active, .tabs-color-royal .tab-item.active, .tabs-color-royal .tab-item.activated { + color: #886aea; + border: 0 solid #886aea; } + .tabs-color-royal .tab-item.tab-item-active .badge, .tabs-color-royal .tab-item.active .badge, .tabs-color-royal .tab-item.activated .badge { + opacity: 1; } + +.tabs-color-dark .tab-item { + color: rgba(68, 68, 68, 0.4); + opacity: 1; } + .tabs-color-dark .tab-item .badge { + opacity: 0.4; } + .tabs-color-dark .tab-item.tab-item-active, .tabs-color-dark .tab-item.active, .tabs-color-dark .tab-item.activated { + color: #444; + border: 0 solid #444; } + .tabs-color-dark .tab-item.tab-item-active .badge, .tabs-color-dark .tab-item.active .badge, .tabs-color-dark .tab-item.activated .badge { + opacity: 1; } + +ion-tabs.tabs-color-active-light .tab-item { + color: #444; } + ion-tabs.tabs-color-active-light .tab-item.tab-item-active, ion-tabs.tabs-color-active-light .tab-item.active, ion-tabs.tabs-color-active-light .tab-item.activated { + color: #fff; } + +ion-tabs.tabs-striped.tabs-color-active-light .tab-item.tab-item-active, ion-tabs.tabs-striped.tabs-color-active-light .tab-item.active, ion-tabs.tabs-striped.tabs-color-active-light .tab-item.activated { + border-color: #fff; + color: #fff; } + +ion-tabs.tabs-color-active-stable .tab-item { + color: #444; } + ion-tabs.tabs-color-active-stable .tab-item.tab-item-active, ion-tabs.tabs-color-active-stable .tab-item.active, ion-tabs.tabs-color-active-stable .tab-item.activated { + color: #f8f8f8; } + +ion-tabs.tabs-striped.tabs-color-active-stable .tab-item.tab-item-active, ion-tabs.tabs-striped.tabs-color-active-stable .tab-item.active, ion-tabs.tabs-striped.tabs-color-active-stable .tab-item.activated { + border-color: #f8f8f8; + color: #f8f8f8; } + +ion-tabs.tabs-color-active-positive .tab-item { + color: #444; } + ion-tabs.tabs-color-active-positive .tab-item.tab-item-active, ion-tabs.tabs-color-active-positive .tab-item.active, ion-tabs.tabs-color-active-positive .tab-item.activated { + color: #387ef5; } + +ion-tabs.tabs-striped.tabs-color-active-positive .tab-item.tab-item-active, ion-tabs.tabs-striped.tabs-color-active-positive .tab-item.active, ion-tabs.tabs-striped.tabs-color-active-positive .tab-item.activated { + border-color: #387ef5; + color: #387ef5; } + +ion-tabs.tabs-color-active-calm .tab-item { + color: #444; } + ion-tabs.tabs-color-active-calm .tab-item.tab-item-active, ion-tabs.tabs-color-active-calm .tab-item.active, ion-tabs.tabs-color-active-calm .tab-item.activated { + color: #11c1f3; } + +ion-tabs.tabs-striped.tabs-color-active-calm .tab-item.tab-item-active, ion-tabs.tabs-striped.tabs-color-active-calm .tab-item.active, ion-tabs.tabs-striped.tabs-color-active-calm .tab-item.activated { + border-color: #11c1f3; + color: #11c1f3; } + +ion-tabs.tabs-color-active-assertive .tab-item { + color: #444; } + ion-tabs.tabs-color-active-assertive .tab-item.tab-item-active, ion-tabs.tabs-color-active-assertive .tab-item.active, ion-tabs.tabs-color-active-assertive .tab-item.activated { + color: #ef473a; } + +ion-tabs.tabs-striped.tabs-color-active-assertive .tab-item.tab-item-active, ion-tabs.tabs-striped.tabs-color-active-assertive .tab-item.active, ion-tabs.tabs-striped.tabs-color-active-assertive .tab-item.activated { + border-color: #ef473a; + color: #ef473a; } + +ion-tabs.tabs-color-active-balanced .tab-item { + color: #444; } + ion-tabs.tabs-color-active-balanced .tab-item.tab-item-active, ion-tabs.tabs-color-active-balanced .tab-item.active, ion-tabs.tabs-color-active-balanced .tab-item.activated { + color: #33cd5f; } + +ion-tabs.tabs-striped.tabs-color-active-balanced .tab-item.tab-item-active, ion-tabs.tabs-striped.tabs-color-active-balanced .tab-item.active, ion-tabs.tabs-striped.tabs-color-active-balanced .tab-item.activated { + border-color: #33cd5f; + color: #33cd5f; } + +ion-tabs.tabs-color-active-energized .tab-item { + color: #444; } + ion-tabs.tabs-color-active-energized .tab-item.tab-item-active, ion-tabs.tabs-color-active-energized .tab-item.active, ion-tabs.tabs-color-active-energized .tab-item.activated { + color: #ffc900; } + +ion-tabs.tabs-striped.tabs-color-active-energized .tab-item.tab-item-active, ion-tabs.tabs-striped.tabs-color-active-energized .tab-item.active, ion-tabs.tabs-striped.tabs-color-active-energized .tab-item.activated { + border-color: #ffc900; + color: #ffc900; } + +ion-tabs.tabs-color-active-royal .tab-item { + color: #444; } + ion-tabs.tabs-color-active-royal .tab-item.tab-item-active, ion-tabs.tabs-color-active-royal .tab-item.active, ion-tabs.tabs-color-active-royal .tab-item.activated { + color: #886aea; } + +ion-tabs.tabs-striped.tabs-color-active-royal .tab-item.tab-item-active, ion-tabs.tabs-striped.tabs-color-active-royal .tab-item.active, ion-tabs.tabs-striped.tabs-color-active-royal .tab-item.activated { + border-color: #886aea; + color: #886aea; } + +ion-tabs.tabs-color-active-dark .tab-item { + color: #fff; } + ion-tabs.tabs-color-active-dark .tab-item.tab-item-active, ion-tabs.tabs-color-active-dark .tab-item.active, ion-tabs.tabs-color-active-dark .tab-item.activated { + color: #444; } + +ion-tabs.tabs-striped.tabs-color-active-dark .tab-item.tab-item-active, ion-tabs.tabs-striped.tabs-color-active-dark .tab-item.active, ion-tabs.tabs-striped.tabs-color-active-dark .tab-item.activated { + border-color: #444; + color: #444; } + +.tabs-top.tabs-striped { + padding-bottom: 0; } + .tabs-top.tabs-striped .tab-item { + background: transparent; + -webkit-transition: color .1s ease; + -moz-transition: color .1s ease; + -ms-transition: color .1s ease; + -o-transition: color .1s ease; + transition: color .1s ease; } + .tabs-top.tabs-striped .tab-item.tab-item-active, .tabs-top.tabs-striped .tab-item.active, .tabs-top.tabs-striped .tab-item.activated { + margin-top: 1px; + border-width: 0px 0px 2px 0px !important; + border-style: solid; } + .tabs-top.tabs-striped .tab-item.tab-item-active > .badge, .tabs-top.tabs-striped .tab-item.tab-item-active > i, .tabs-top.tabs-striped .tab-item.active > .badge, .tabs-top.tabs-striped .tab-item.active > i, .tabs-top.tabs-striped .tab-item.activated > .badge, .tabs-top.tabs-striped .tab-item.activated > i { + margin-top: -1px; } + .tabs-top.tabs-striped .tab-item .badge { + -webkit-transition: color .2s ease; + -moz-transition: color .2s ease; + -ms-transition: color .2s ease; + -o-transition: color .2s ease; + transition: color .2s ease; } + .tabs-top.tabs-striped:not(.tabs-icon-left):not(.tabs-icon-top) .tab-item.tab-item-active .tab-title, .tabs-top.tabs-striped:not(.tabs-icon-left):not(.tabs-icon-top) .tab-item.tab-item-active i, .tabs-top.tabs-striped:not(.tabs-icon-left):not(.tabs-icon-top) .tab-item.active .tab-title, .tabs-top.tabs-striped:not(.tabs-icon-left):not(.tabs-icon-top) .tab-item.active i, .tabs-top.tabs-striped:not(.tabs-icon-left):not(.tabs-icon-top) .tab-item.activated .tab-title, .tabs-top.tabs-striped:not(.tabs-icon-left):not(.tabs-icon-top) .tab-item.activated i { + display: block; + margin-top: -1px; } + .tabs-top.tabs-striped.tabs-icon-left .tab-item { + margin-top: 1px; } + .tabs-top.tabs-striped.tabs-icon-left .tab-item.tab-item-active .tab-title, .tabs-top.tabs-striped.tabs-icon-left .tab-item.tab-item-active i, .tabs-top.tabs-striped.tabs-icon-left .tab-item.active .tab-title, .tabs-top.tabs-striped.tabs-icon-left .tab-item.active i, .tabs-top.tabs-striped.tabs-icon-left .tab-item.activated .tab-title, .tabs-top.tabs-striped.tabs-icon-left .tab-item.activated i { + margin-top: -0.1em; } + +/* Allow parent element to have tabs-top */ +/* If you change this, change platform.scss as well */ +.tabs-top > .tabs, +.tabs.tabs-top { + top: 44px; + padding-top: 0; + background-position: bottom; + border-top-width: 0; + border-bottom-width: 1px; } + .tabs-top > .tabs .tab-item.tab-item-active .badge, .tabs-top > .tabs .tab-item.active .badge, .tabs-top > .tabs .tab-item.activated .badge, + .tabs.tabs-top .tab-item.tab-item-active .badge, + .tabs.tabs-top .tab-item.active .badge, + .tabs.tabs-top .tab-item.activated .badge { + top: 4%; } + +.tabs-top ~ .bar-header { + border-bottom-width: 0; } + +.tab-item { + -webkit-box-flex: 1; + -webkit-flex: 1; + -moz-box-flex: 1; + -moz-flex: 1; + -ms-flex: 1; + flex: 1; + display: block; + overflow: hidden; + max-width: 150px; + height: 100%; + color: inherit; + text-align: center; + text-decoration: none; + text-overflow: ellipsis; + white-space: nowrap; + font-weight: 400; + font-size: 14px; + font-family: "-apple-system", "Helvetica Neue", "Roboto", "Segoe UI", sans-serif; + opacity: 0.7; } + .tab-item:hover { + cursor: pointer; } + .tab-item.tab-hidden { + display: none; } + +.tabs-item-hide > .tabs, +.tabs.tabs-item-hide { + display: none; } + +.tabs-icon-top > .tabs .tab-item, +.tabs-icon-top.tabs .tab-item, +.tabs-icon-bottom > .tabs .tab-item, +.tabs-icon-bottom.tabs .tab-item { + font-size: 10px; + line-height: 14px; } + +.tab-item .icon { + display: block; + margin: 0 auto; + height: 32px; + font-size: 32px; } + +.tabs-icon-left.tabs .tab-item, +.tabs-icon-left > .tabs .tab-item, +.tabs-icon-right.tabs .tab-item, +.tabs-icon-right > .tabs .tab-item { + font-size: 10px; } + .tabs-icon-left.tabs .tab-item .icon, .tabs-icon-left.tabs .tab-item .tab-title, + .tabs-icon-left > .tabs .tab-item .icon, + .tabs-icon-left > .tabs .tab-item .tab-title, + .tabs-icon-right.tabs .tab-item .icon, + .tabs-icon-right.tabs .tab-item .tab-title, + .tabs-icon-right > .tabs .tab-item .icon, + .tabs-icon-right > .tabs .tab-item .tab-title { + display: inline-block; + vertical-align: top; + margin-top: -.1em; } + .tabs-icon-left.tabs .tab-item .icon:before, .tabs-icon-left.tabs .tab-item .tab-title:before, + .tabs-icon-left > .tabs .tab-item .icon:before, + .tabs-icon-left > .tabs .tab-item .tab-title:before, + .tabs-icon-right.tabs .tab-item .icon:before, + .tabs-icon-right.tabs .tab-item .tab-title:before, + .tabs-icon-right > .tabs .tab-item .icon:before, + .tabs-icon-right > .tabs .tab-item .tab-title:before { + font-size: 24px; + line-height: 49px; } + +.tabs-icon-left > .tabs .tab-item .icon, +.tabs-icon-left.tabs .tab-item .icon { + padding-right: 3px; } + +.tabs-icon-right > .tabs .tab-item .icon, +.tabs-icon-right.tabs .tab-item .icon { + padding-left: 3px; } + +.tabs-icon-only > .tabs .icon, +.tabs-icon-only.tabs .icon { + line-height: inherit; } + +.tab-item.has-badge { + position: relative; } + +.tab-item .badge { + position: absolute; + top: 4%; + right: 33%; + right: calc(50% - 26px); + padding: 1px 6px; + height: auto; + font-size: 12px; + line-height: 16px; } + +/* Navigational tab */ +/* Active state for tab */ +.tab-item.tab-item-active, +.tab-item.active, +.tab-item.activated { + opacity: 1; } + .tab-item.tab-item-active.tab-item-light, + .tab-item.active.tab-item-light, + .tab-item.activated.tab-item-light { + color: #fff; } + .tab-item.tab-item-active.tab-item-stable, + .tab-item.active.tab-item-stable, + .tab-item.activated.tab-item-stable { + color: #f8f8f8; } + .tab-item.tab-item-active.tab-item-positive, + .tab-item.active.tab-item-positive, + .tab-item.activated.tab-item-positive { + color: #387ef5; } + .tab-item.tab-item-active.tab-item-calm, + .tab-item.active.tab-item-calm, + .tab-item.activated.tab-item-calm { + color: #11c1f3; } + .tab-item.tab-item-active.tab-item-assertive, + .tab-item.active.tab-item-assertive, + .tab-item.activated.tab-item-assertive { + color: #ef473a; } + .tab-item.tab-item-active.tab-item-balanced, + .tab-item.active.tab-item-balanced, + .tab-item.activated.tab-item-balanced { + color: #33cd5f; } + .tab-item.tab-item-active.tab-item-energized, + .tab-item.active.tab-item-energized, + .tab-item.activated.tab-item-energized { + color: #ffc900; } + .tab-item.tab-item-active.tab-item-royal, + .tab-item.active.tab-item-royal, + .tab-item.activated.tab-item-royal { + color: #886aea; } + .tab-item.tab-item-active.tab-item-dark, + .tab-item.active.tab-item-dark, + .tab-item.activated.tab-item-dark { + color: #444; } + +.item.tabs { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + padding: 0; } + .item.tabs .icon:before { + position: relative; } + +.tab-item.disabled, +.tab-item[disabled] { + opacity: .4; + cursor: default; + pointer-events: none; } + +.nav-bar-tabs-top.hide ~ .view-container .tabs-top .tabs { + top: 0; } + +.pane[hide-nav-bar="true"] .has-tabs-top { + top: 49px; } + +/** + * Menus + * -------------------------------------------------- + * Side panel structure + */ +.menu { + position: absolute; + top: 0; + bottom: 0; + z-index: 0; + overflow: hidden; + min-height: 100%; + max-height: 100%; + width: 275px; + background-color: #fff; } + .menu .scroll-content { + z-index: 10; } + .menu .bar-header { + z-index: 11; } + +.menu-content { + -webkit-transform: none; + transform: none; + box-shadow: -1px 0px 2px rgba(0, 0, 0, 0.2), 1px 0px 2px rgba(0, 0, 0, 0.2); } + +.menu-open .menu-content .pane, +.menu-open .menu-content .scroll-content { + pointer-events: none; } + +.menu-open .menu-content .scroll-content .scroll { + pointer-events: none; } + +.menu-open .menu-content .scroll-content:not(.overflow-scroll) { + overflow: hidden; } + +.grade-b .menu-content, +.grade-c .menu-content { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + right: -1px; + left: -1px; + border-right: 1px solid #ccc; + border-left: 1px solid #ccc; + box-shadow: none; } + +.menu-left { + left: 0; } + +.menu-right { + right: 0; } + +.aside-open.aside-resizing .menu-right { + display: none; } + +.menu-animated { + -webkit-transition: -webkit-transform 200ms ease; + transition: transform 200ms ease; } + +/** + * Modals + * -------------------------------------------------- + * Modals are independent windows that slide in from off-screen. + */ +.modal-backdrop, +.modal-backdrop-bg { + position: fixed; + top: 0; + left: 0; + z-index: 10; + width: 100%; + height: 100%; } + +.modal-backdrop-bg { + pointer-events: none; } + +.modal { + display: block; + position: absolute; + top: 0; + z-index: 10; + overflow: hidden; + min-height: 100%; + width: 100%; + background-color: #fff; } + +@media (min-width: 680px) { + .modal { + top: 20%; + right: 20%; + bottom: 20%; + left: 20%; + min-height: 240px; + width: 60%; } + .modal.ng-leave-active { + bottom: 0; } + .platform-ios.platform-cordova .modal-wrapper .modal .bar-header:not(.bar-subheader) { + height: 44px; } + .platform-ios.platform-cordova .modal-wrapper .modal .bar-header:not(.bar-subheader) > * { + margin-top: 0; } + .platform-ios.platform-cordova .modal-wrapper .modal .tabs-top > .tabs, + .platform-ios.platform-cordova .modal-wrapper .modal .tabs.tabs-top { + top: 44px; } + .platform-ios.platform-cordova .modal-wrapper .modal .has-header, + .platform-ios.platform-cordova .modal-wrapper .modal .bar-subheader { + top: 44px; } + .platform-ios.platform-cordova .modal-wrapper .modal .has-subheader { + top: 88px; } + .platform-ios.platform-cordova .modal-wrapper .modal .has-header.has-tabs-top { + top: 93px; } + .platform-ios.platform-cordova .modal-wrapper .modal .has-header.has-subheader.has-tabs-top { + top: 137px; } + .modal-backdrop-bg { + -webkit-transition: opacity 300ms ease-in-out; + transition: opacity 300ms ease-in-out; + background-color: #000; + opacity: 0; } + .active .modal-backdrop-bg { + opacity: 0.5; } } + +.modal-open { + pointer-events: none; } + .modal-open .modal, + .modal-open .modal-backdrop { + pointer-events: auto; } + .modal-open.loading-active .modal, + .modal-open.loading-active .modal-backdrop { + pointer-events: none; } + +/** + * Popovers + * -------------------------------------------------- + * Popovers are independent views which float over content + */ +.popover-backdrop { + position: fixed; + top: 0; + left: 0; + z-index: 10; + width: 100%; + height: 100%; + background-color: transparent; } + .popover-backdrop.active { + background-color: rgba(0, 0, 0, 0.1); } + +.popover { + position: absolute; + top: 25%; + left: 50%; + z-index: 10; + display: block; + margin-top: 12px; + margin-left: -110px; + height: 280px; + width: 220px; + background-color: #fff; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4); + opacity: 0; } + .popover .item:first-child { + border-top: 0; } + .popover .item:last-child { + border-bottom: 0; } + .popover.popover-bottom { + margin-top: -12px; } + +.popover, +.popover .bar-header { + border-radius: 2px; } + +.popover .scroll-content { + z-index: 1; + margin: 2px 0; } + +.popover .bar-header { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; } + +.popover .has-header { + border-top-right-radius: 0; + border-top-left-radius: 0; } + +.popover-arrow { + display: none; } + +.platform-ios .popover { + box-shadow: 0 0 40px rgba(0, 0, 0, 0.08); + border-radius: 10px; } + +.platform-ios .popover .bar-header { + -webkit-border-top-right-radius: 10px; + border-top-right-radius: 10px; + -webkit-border-top-left-radius: 10px; + border-top-left-radius: 10px; } + +.platform-ios .popover .scroll-content { + margin: 8px 0; + border-radius: 10px; } + +.platform-ios .popover .scroll-content.has-header { + margin-top: 0; } + +.platform-ios .popover-arrow { + position: absolute; + display: block; + top: -17px; + width: 30px; + height: 19px; + overflow: hidden; } + .platform-ios .popover-arrow:after { + position: absolute; + top: 12px; + left: 5px; + width: 20px; + height: 20px; + background-color: #fff; + border-radius: 3px; + content: ''; + -webkit-transform: rotate(-45deg); + transform: rotate(-45deg); } + +.platform-ios .popover-bottom .popover-arrow { + top: auto; + bottom: -10px; } + .platform-ios .popover-bottom .popover-arrow:after { + top: -6px; } + +.platform-android .popover { + margin-top: -32px; + background-color: #fafafa; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.35); } + .platform-android .popover .item { + border-color: #fafafa; + background-color: #fafafa; + color: #4d4d4d; } + .platform-android .popover.popover-bottom { + margin-top: 32px; } + +.platform-android .popover-backdrop, +.platform-android .popover-backdrop.active { + background-color: transparent; } + +.popover-open { + pointer-events: none; } + .popover-open .popover, + .popover-open .popover-backdrop { + pointer-events: auto; } + .popover-open.loading-active .popover, + .popover-open.loading-active .popover-backdrop { + pointer-events: none; } + +@media (min-width: 680px) { + .popover { + width: 360px; + margin-left: -180px; } } + +/** + * Popups + * -------------------------------------------------- + */ +.popup-container { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + background: transparent; + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + -moz-justify-content: center; + justify-content: center; + -webkit-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + -moz-align-items: center; + align-items: center; + z-index: 12; + visibility: hidden; } + .popup-container.popup-showing { + visibility: visible; } + .popup-container.popup-hidden .popup { + -webkit-animation-name: scaleOut; + animation-name: scaleOut; + -webkit-animation-duration: 0.1s; + animation-duration: 0.1s; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; } + .popup-container.active .popup { + -webkit-animation-name: superScaleIn; + animation-name: superScaleIn; + -webkit-animation-duration: 0.2s; + animation-duration: 0.2s; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; } + .popup-container .popup { + width: 250px; + max-width: 100%; + max-height: 90%; + border-radius: 0px; + background-color: rgba(255, 255, 255, 0.9); + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-direction: normal; + -webkit-box-orient: vertical; + -webkit-flex-direction: column; + -moz-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; } + .popup-container input, + .popup-container textarea { + width: 100%; } + +.popup-head { + padding: 15px 10px; + border-bottom: 1px solid #eee; + text-align: center; } + +.popup-title { + margin: 0; + padding: 0; + font-size: 15px; } + +.popup-sub-title { + margin: 5px 0 0 0; + padding: 0; + font-weight: normal; + font-size: 11px; } + +.popup-body { + padding: 10px; + overflow: auto; } + +.popup-buttons { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-direction: normal; + -webkit-box-orient: horizontal; + -webkit-flex-direction: row; + -moz-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + padding: 10px; + min-height: 65px; } + .popup-buttons .button { + -webkit-box-flex: 1; + -webkit-flex: 1; + -moz-box-flex: 1; + -moz-flex: 1; + -ms-flex: 1; + flex: 1; + display: block; + min-height: 45px; + border-radius: 2px; + line-height: 20px; + margin-right: 5px; } + .popup-buttons .button:last-child { + margin-right: 0px; } + +.popup-open { + pointer-events: none; } + .popup-open.modal-open .modal { + pointer-events: none; } + .popup-open .popup-backdrop, .popup-open .popup { + pointer-events: auto; } + +/** + * Loading + * -------------------------------------------------- + */ +.loading-container { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + z-index: 13; + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + -moz-justify-content: center; + justify-content: center; + -webkit-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + -moz-align-items: center; + align-items: center; + -webkit-transition: 0.2s opacity linear; + transition: 0.2s opacity linear; + visibility: hidden; + opacity: 0; } + .loading-container:not(.visible) .icon, + .loading-container:not(.visible) .spinner { + display: none; } + .loading-container.visible { + visibility: visible; } + .loading-container.active { + opacity: 1; } + .loading-container .loading { + padding: 20px; + border-radius: 5px; + background-color: rgba(0, 0, 0, 0.7); + color: #fff; + text-align: center; + text-overflow: ellipsis; + font-size: 15px; } + .loading-container .loading h1, .loading-container .loading h2, .loading-container .loading h3, .loading-container .loading h4, .loading-container .loading h5, .loading-container .loading h6 { + color: #fff; } + +/** + * Items + * -------------------------------------------------- + */ +.item { + border-color: #ddd; + background-color: #fff; + color: #444; + position: relative; + z-index: 2; + display: block; + margin: -1px; + padding: 16px; + border-width: 1px; + border-style: solid; + font-size: 16px; } + .item h2 { + margin: 0 0 2px 0; + font-size: 16px; + font-weight: normal; } + .item h3 { + margin: 0 0 4px 0; + font-size: 14px; } + .item h4 { + margin: 0 0 4px 0; + font-size: 12px; } + .item h5, .item h6 { + margin: 0 0 3px 0; + font-size: 10px; } + .item p { + color: #666; + font-size: 14px; + margin-bottom: 2px; } + .item h1:last-child, + .item h2:last-child, + .item h3:last-child, + .item h4:last-child, + .item h5:last-child, + .item h6:last-child, + .item p:last-child { + margin-bottom: 0; } + .item .badge { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + position: absolute; + top: 16px; + right: 32px; } + .item.item-button-right .badge { + right: 67px; } + .item.item-divider .badge { + top: 8px; } + .item .badge + .badge { + margin-right: 5px; } + .item.item-light { + border-color: #ddd; + background-color: #fff; + color: #444; } + .item.item-stable { + border-color: #b2b2b2; + background-color: #f8f8f8; + color: #444; } + .item.item-positive { + border-color: #0c60ee; + background-color: #387ef5; + color: #fff; } + .item.item-calm { + border-color: #0a9dc7; + background-color: #11c1f3; + color: #fff; } + .item.item-assertive { + border-color: #e42112; + background-color: #ef473a; + color: #fff; } + .item.item-balanced { + border-color: #28a54c; + background-color: #33cd5f; + color: #fff; } + .item.item-energized { + border-color: #e6b500; + background-color: #ffc900; + color: #fff; } + .item.item-royal { + border-color: #6b46e5; + background-color: #886aea; + color: #fff; } + .item.item-dark { + border-color: #111; + background-color: #444; + color: #fff; } + .item[ng-click]:hover { + cursor: pointer; } + +.list-borderless .item, +.item-borderless { + border-width: 0; } + +.item.active, +.item.activated, +.item-complex.active .item-content, +.item-complex.activated .item-content, +.item .item-content.active, +.item .item-content.activated { + border-color: #ccc; + background-color: #D9D9D9; } + .item.active.item-complex > .item-content, + .item.activated.item-complex > .item-content, + .item-complex.active .item-content.item-complex > .item-content, + .item-complex.activated .item-content.item-complex > .item-content, + .item .item-content.active.item-complex > .item-content, + .item .item-content.activated.item-complex > .item-content { + border-color: #ccc; + background-color: #D9D9D9; } + .item.active.item-light, + .item.activated.item-light, + .item-complex.active .item-content.item-light, + .item-complex.activated .item-content.item-light, + .item .item-content.active.item-light, + .item .item-content.activated.item-light { + border-color: #ccc; + background-color: #fafafa; } + .item.active.item-light.item-complex > .item-content, + .item.activated.item-light.item-complex > .item-content, + .item-complex.active .item-content.item-light.item-complex > .item-content, + .item-complex.activated .item-content.item-light.item-complex > .item-content, + .item .item-content.active.item-light.item-complex > .item-content, + .item .item-content.activated.item-light.item-complex > .item-content { + border-color: #ccc; + background-color: #fafafa; } + .item.active.item-stable, + .item.activated.item-stable, + .item-complex.active .item-content.item-stable, + .item-complex.activated .item-content.item-stable, + .item .item-content.active.item-stable, + .item .item-content.activated.item-stable { + border-color: #a2a2a2; + background-color: #e5e5e5; } + .item.active.item-stable.item-complex > .item-content, + .item.activated.item-stable.item-complex > .item-content, + .item-complex.active .item-content.item-stable.item-complex > .item-content, + .item-complex.activated .item-content.item-stable.item-complex > .item-content, + .item .item-content.active.item-stable.item-complex > .item-content, + .item .item-content.activated.item-stable.item-complex > .item-content { + border-color: #a2a2a2; + background-color: #e5e5e5; } + .item.active.item-positive, + .item.activated.item-positive, + .item-complex.active .item-content.item-positive, + .item-complex.activated .item-content.item-positive, + .item .item-content.active.item-positive, + .item .item-content.activated.item-positive { + border-color: #0c60ee; + background-color: #0c60ee; } + .item.active.item-positive.item-complex > .item-content, + .item.activated.item-positive.item-complex > .item-content, + .item-complex.active .item-content.item-positive.item-complex > .item-content, + .item-complex.activated .item-content.item-positive.item-complex > .item-content, + .item .item-content.active.item-positive.item-complex > .item-content, + .item .item-content.activated.item-positive.item-complex > .item-content { + border-color: #0c60ee; + background-color: #0c60ee; } + .item.active.item-calm, + .item.activated.item-calm, + .item-complex.active .item-content.item-calm, + .item-complex.activated .item-content.item-calm, + .item .item-content.active.item-calm, + .item .item-content.activated.item-calm { + border-color: #0a9dc7; + background-color: #0a9dc7; } + .item.active.item-calm.item-complex > .item-content, + .item.activated.item-calm.item-complex > .item-content, + .item-complex.active .item-content.item-calm.item-complex > .item-content, + .item-complex.activated .item-content.item-calm.item-complex > .item-content, + .item .item-content.active.item-calm.item-complex > .item-content, + .item .item-content.activated.item-calm.item-complex > .item-content { + border-color: #0a9dc7; + background-color: #0a9dc7; } + .item.active.item-assertive, + .item.activated.item-assertive, + .item-complex.active .item-content.item-assertive, + .item-complex.activated .item-content.item-assertive, + .item .item-content.active.item-assertive, + .item .item-content.activated.item-assertive { + border-color: #e42112; + background-color: #e42112; } + .item.active.item-assertive.item-complex > .item-content, + .item.activated.item-assertive.item-complex > .item-content, + .item-complex.active .item-content.item-assertive.item-complex > .item-content, + .item-complex.activated .item-content.item-assertive.item-complex > .item-content, + .item .item-content.active.item-assertive.item-complex > .item-content, + .item .item-content.activated.item-assertive.item-complex > .item-content { + border-color: #e42112; + background-color: #e42112; } + .item.active.item-balanced, + .item.activated.item-balanced, + .item-complex.active .item-content.item-balanced, + .item-complex.activated .item-content.item-balanced, + .item .item-content.active.item-balanced, + .item .item-content.activated.item-balanced { + border-color: #28a54c; + background-color: #28a54c; } + .item.active.item-balanced.item-complex > .item-content, + .item.activated.item-balanced.item-complex > .item-content, + .item-complex.active .item-content.item-balanced.item-complex > .item-content, + .item-complex.activated .item-content.item-balanced.item-complex > .item-content, + .item .item-content.active.item-balanced.item-complex > .item-content, + .item .item-content.activated.item-balanced.item-complex > .item-content { + border-color: #28a54c; + background-color: #28a54c; } + .item.active.item-energized, + .item.activated.item-energized, + .item-complex.active .item-content.item-energized, + .item-complex.activated .item-content.item-energized, + .item .item-content.active.item-energized, + .item .item-content.activated.item-energized { + border-color: #e6b500; + background-color: #e6b500; } + .item.active.item-energized.item-complex > .item-content, + .item.activated.item-energized.item-complex > .item-content, + .item-complex.active .item-content.item-energized.item-complex > .item-content, + .item-complex.activated .item-content.item-energized.item-complex > .item-content, + .item .item-content.active.item-energized.item-complex > .item-content, + .item .item-content.activated.item-energized.item-complex > .item-content { + border-color: #e6b500; + background-color: #e6b500; } + .item.active.item-royal, + .item.activated.item-royal, + .item-complex.active .item-content.item-royal, + .item-complex.activated .item-content.item-royal, + .item .item-content.active.item-royal, + .item .item-content.activated.item-royal { + border-color: #6b46e5; + background-color: #6b46e5; } + .item.active.item-royal.item-complex > .item-content, + .item.activated.item-royal.item-complex > .item-content, + .item-complex.active .item-content.item-royal.item-complex > .item-content, + .item-complex.activated .item-content.item-royal.item-complex > .item-content, + .item .item-content.active.item-royal.item-complex > .item-content, + .item .item-content.activated.item-royal.item-complex > .item-content { + border-color: #6b46e5; + background-color: #6b46e5; } + .item.active.item-dark, + .item.activated.item-dark, + .item-complex.active .item-content.item-dark, + .item-complex.activated .item-content.item-dark, + .item .item-content.active.item-dark, + .item .item-content.activated.item-dark { + border-color: #000; + background-color: #262626; } + .item.active.item-dark.item-complex > .item-content, + .item.activated.item-dark.item-complex > .item-content, + .item-complex.active .item-content.item-dark.item-complex > .item-content, + .item-complex.activated .item-content.item-dark.item-complex > .item-content, + .item .item-content.active.item-dark.item-complex > .item-content, + .item .item-content.activated.item-dark.item-complex > .item-content { + border-color: #000; + background-color: #262626; } + +.item, +.item h1, +.item h2, +.item h3, +.item h4, +.item h5, +.item h6, +.item p, +.item-content, +.item-content h1, +.item-content h2, +.item-content h3, +.item-content h4, +.item-content h5, +.item-content h6, +.item-content p { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } + +a.item { + color: inherit; + text-decoration: none; } + a.item:hover, a.item:focus { + text-decoration: none; } + +/** + * Complex Items + * -------------------------------------------------- + * Adding .item-complex allows the .item to be slidable and + * have options underneath the button, but also requires an + * additional .item-content element inside .item. + * Basically .item-complex removes any default settings which + * .item added, so that .item-content looks them as just .item. + */ +.item-complex, +a.item.item-complex, +button.item.item-complex { + padding: 0; } + +.item-complex .item-content, +.item-radio .item-content { + position: relative; + z-index: 2; + padding: 16px 49px 16px 16px; + border: none; + background-color: #fff; } + +a.item-content { + display: block; + color: inherit; + text-decoration: none; } + +.item-text-wrap .item, +.item-text-wrap .item-content, +.item-text-wrap, +.item-text-wrap h1, +.item-text-wrap h2, +.item-text-wrap h3, +.item-text-wrap h4, +.item-text-wrap h5, +.item-text-wrap h6, +.item-text-wrap p, +.item-complex.item-text-wrap .item-content, +.item-body h1, +.item-body h2, +.item-body h3, +.item-body h4, +.item-body h5, +.item-body h6, +.item-body p { + overflow: visible; + white-space: normal; } + +.item-complex.item-text-wrap, +.item-complex.item-text-wrap h1, +.item-complex.item-text-wrap h2, +.item-complex.item-text-wrap h3, +.item-complex.item-text-wrap h4, +.item-complex.item-text-wrap h5, +.item-complex.item-text-wrap h6, +.item-complex.item-text-wrap p { + overflow: visible; + white-space: normal; } + +.item-complex.item-light > .item-content { + border-color: #ddd; + background-color: #fff; + color: #444; } + .item-complex.item-light > .item-content.active, .item-complex.item-light > .item-content:active { + border-color: #ccc; + background-color: #fafafa; } + .item-complex.item-light > .item-content.active.item-complex > .item-content, .item-complex.item-light > .item-content:active.item-complex > .item-content { + border-color: #ccc; + background-color: #fafafa; } + +.item-complex.item-stable > .item-content { + border-color: #b2b2b2; + background-color: #f8f8f8; + color: #444; } + .item-complex.item-stable > .item-content.active, .item-complex.item-stable > .item-content:active { + border-color: #a2a2a2; + background-color: #e5e5e5; } + .item-complex.item-stable > .item-content.active.item-complex > .item-content, .item-complex.item-stable > .item-content:active.item-complex > .item-content { + border-color: #a2a2a2; + background-color: #e5e5e5; } + +.item-complex.item-positive > .item-content { + border-color: #0c60ee; + background-color: #387ef5; + color: #fff; } + .item-complex.item-positive > .item-content.active, .item-complex.item-positive > .item-content:active { + border-color: #0c60ee; + background-color: #0c60ee; } + .item-complex.item-positive > .item-content.active.item-complex > .item-content, .item-complex.item-positive > .item-content:active.item-complex > .item-content { + border-color: #0c60ee; + background-color: #0c60ee; } + +.item-complex.item-calm > .item-content { + border-color: #0a9dc7; + background-color: #11c1f3; + color: #fff; } + .item-complex.item-calm > .item-content.active, .item-complex.item-calm > .item-content:active { + border-color: #0a9dc7; + background-color: #0a9dc7; } + .item-complex.item-calm > .item-content.active.item-complex > .item-content, .item-complex.item-calm > .item-content:active.item-complex > .item-content { + border-color: #0a9dc7; + background-color: #0a9dc7; } + +.item-complex.item-assertive > .item-content { + border-color: #e42112; + background-color: #ef473a; + color: #fff; } + .item-complex.item-assertive > .item-content.active, .item-complex.item-assertive > .item-content:active { + border-color: #e42112; + background-color: #e42112; } + .item-complex.item-assertive > .item-content.active.item-complex > .item-content, .item-complex.item-assertive > .item-content:active.item-complex > .item-content { + border-color: #e42112; + background-color: #e42112; } + +.item-complex.item-balanced > .item-content { + border-color: #28a54c; + background-color: #33cd5f; + color: #fff; } + .item-complex.item-balanced > .item-content.active, .item-complex.item-balanced > .item-content:active { + border-color: #28a54c; + background-color: #28a54c; } + .item-complex.item-balanced > .item-content.active.item-complex > .item-content, .item-complex.item-balanced > .item-content:active.item-complex > .item-content { + border-color: #28a54c; + background-color: #28a54c; } + +.item-complex.item-energized > .item-content { + border-color: #e6b500; + background-color: #ffc900; + color: #fff; } + .item-complex.item-energized > .item-content.active, .item-complex.item-energized > .item-content:active { + border-color: #e6b500; + background-color: #e6b500; } + .item-complex.item-energized > .item-content.active.item-complex > .item-content, .item-complex.item-energized > .item-content:active.item-complex > .item-content { + border-color: #e6b500; + background-color: #e6b500; } + +.item-complex.item-royal > .item-content { + border-color: #6b46e5; + background-color: #886aea; + color: #fff; } + .item-complex.item-royal > .item-content.active, .item-complex.item-royal > .item-content:active { + border-color: #6b46e5; + background-color: #6b46e5; } + .item-complex.item-royal > .item-content.active.item-complex > .item-content, .item-complex.item-royal > .item-content:active.item-complex > .item-content { + border-color: #6b46e5; + background-color: #6b46e5; } + +.item-complex.item-dark > .item-content { + border-color: #111; + background-color: #444; + color: #fff; } + .item-complex.item-dark > .item-content.active, .item-complex.item-dark > .item-content:active { + border-color: #000; + background-color: #262626; } + .item-complex.item-dark > .item-content.active.item-complex > .item-content, .item-complex.item-dark > .item-content:active.item-complex > .item-content { + border-color: #000; + background-color: #262626; } + +/** + * Item Icons + * -------------------------------------------------- + */ +.item-icon-left .icon, +.item-icon-right .icon { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + -moz-align-items: center; + align-items: center; + position: absolute; + top: 0; + height: 100%; + font-size: 32px; } + .item-icon-left .icon:before, + .item-icon-right .icon:before { + display: block; + width: 32px; + text-align: center; } + +.item .fill-icon { + min-width: 30px; + min-height: 30px; + font-size: 28px; } + +.item-icon-left { + padding-left: 54px; } + .item-icon-left .icon { + left: 11px; } + +.item-complex.item-icon-left { + padding-left: 0; } + .item-complex.item-icon-left .item-content { + padding-left: 54px; } + +.item-icon-right { + padding-right: 54px; } + .item-icon-right .icon { + right: 11px; } + +.item-complex.item-icon-right { + padding-right: 0; } + .item-complex.item-icon-right .item-content { + padding-right: 54px; } + +.item-icon-left.item-icon-right .icon:first-child { + right: auto; } + +.item-icon-left.item-icon-right .icon:last-child, +.item-icon-left .item-delete .icon { + left: auto; } + +.item-icon-left .icon-accessory, +.item-icon-right .icon-accessory { + color: #ccc; + font-size: 16px; } + +.item-icon-left .icon-accessory { + left: 3px; } + +.item-icon-right .icon-accessory { + right: 3px; } + +/** + * Item Button + * -------------------------------------------------- + * An item button is a child button inside an .item (not the entire .item) + */ +.item-button-left { + padding-left: 72px; } + +.item-button-left > .button, +.item-button-left .item-content > .button { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + -moz-align-items: center; + align-items: center; + position: absolute; + top: 8px; + left: 11px; + min-width: 34px; + min-height: 34px; + font-size: 18px; + line-height: 32px; } + .item-button-left > .button .icon:before, + .item-button-left .item-content > .button .icon:before { + position: relative; + left: auto; + width: auto; + line-height: 31px; } + .item-button-left > .button > .button, + .item-button-left .item-content > .button > .button { + margin: 0px 2px; + min-height: 34px; + font-size: 18px; + line-height: 32px; } + +.item-button-right, +a.item.item-button-right, +button.item.item-button-right { + padding-right: 80px; } + +.item-button-right > .button, +.item-button-right .item-content > .button, +.item-button-right > .buttons, +.item-button-right .item-content > .buttons { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + -moz-align-items: center; + align-items: center; + position: absolute; + top: 8px; + right: 16px; + min-width: 34px; + min-height: 34px; + font-size: 18px; + line-height: 32px; } + .item-button-right > .button .icon:before, + .item-button-right .item-content > .button .icon:before, + .item-button-right > .buttons .icon:before, + .item-button-right .item-content > .buttons .icon:before { + position: relative; + left: auto; + width: auto; + line-height: 31px; } + .item-button-right > .button > .button, + .item-button-right .item-content > .button > .button, + .item-button-right > .buttons > .button, + .item-button-right .item-content > .buttons > .button { + margin: 0px 2px; + min-width: 34px; + min-height: 34px; + font-size: 18px; + line-height: 32px; } + +.item-button-left.item-button-right .button:first-child { + right: auto; } + +.item-button-left.item-button-right .button:last-child { + left: auto; } + +.item-avatar, +.item-avatar .item-content, +.item-avatar-left, +.item-avatar-left .item-content { + padding-left: 72px; + min-height: 72px; } + .item-avatar > img:first-child, + .item-avatar .item-image, + .item-avatar .item-content > img:first-child, + .item-avatar .item-content .item-image, + .item-avatar-left > img:first-child, + .item-avatar-left .item-image, + .item-avatar-left .item-content > img:first-child, + .item-avatar-left .item-content .item-image { + position: absolute; + top: 16px; + left: 16px; + max-width: 40px; + max-height: 40px; + width: 100%; + height: 100%; + border-radius: 50%; } + +.item-avatar-right, +.item-avatar-right .item-content { + padding-right: 72px; + min-height: 72px; } + .item-avatar-right > img:first-child, + .item-avatar-right .item-image, + .item-avatar-right .item-content > img:first-child, + .item-avatar-right .item-content .item-image { + position: absolute; + top: 16px; + right: 16px; + max-width: 40px; + max-height: 40px; + width: 100%; + height: 100%; + border-radius: 50%; } + +.item-thumbnail-left, +.item-thumbnail-left .item-content { + padding-top: 8px; + padding-left: 106px; + min-height: 100px; } + .item-thumbnail-left > img:first-child, + .item-thumbnail-left .item-image, + .item-thumbnail-left .item-content > img:first-child, + .item-thumbnail-left .item-content .item-image { + position: absolute; + top: 10px; + left: 10px; + max-width: 80px; + max-height: 80px; + width: 100%; + height: 100%; } + +.item-avatar.item-complex, +.item-avatar-left.item-complex, +.item-thumbnail-left.item-complex { + padding-top: 0; + padding-left: 0; } + +.item-thumbnail-right, +.item-thumbnail-right .item-content { + padding-top: 8px; + padding-right: 106px; + min-height: 100px; } + .item-thumbnail-right > img:first-child, + .item-thumbnail-right .item-image, + .item-thumbnail-right .item-content > img:first-child, + .item-thumbnail-right .item-content .item-image { + position: absolute; + top: 10px; + right: 10px; + max-width: 80px; + max-height: 80px; + width: 100%; + height: 100%; } + +.item-avatar-right.item-complex, +.item-thumbnail-right.item-complex { + padding-top: 0; + padding-right: 0; } + +.item-image { + padding: 0; + text-align: center; } + .item-image img:first-child, .item-image .list-img { + width: 100%; + vertical-align: middle; } + +.item-body { + overflow: auto; + padding: 16px; + text-overflow: inherit; + white-space: normal; } + .item-body h1, .item-body h2, .item-body h3, .item-body h4, .item-body h5, .item-body h6, .item-body p { + margin-top: 16px; + margin-bottom: 16px; } + +.item-divider { + padding-top: 8px; + padding-bottom: 8px; + min-height: 30px; + background-color: #f5f5f5; + color: #222; + font-weight: 500; } + +.platform-ios .item-divider-platform, +.item-divider-ios { + padding-top: 26px; + text-transform: uppercase; + font-weight: 300; + font-size: 13px; + background-color: #efeff4; + color: #555; } + +.platform-android .item-divider-platform, +.item-divider-android { + font-weight: 300; + font-size: 13px; } + +.item-note { + float: right; + color: #aaa; + font-size: 14px; } + +.item-left-editable .item-content, +.item-right-editable .item-content { + -webkit-transition-duration: 250ms; + transition-duration: 250ms; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -webkit-transition-property: -webkit-transform; + -moz-transition-property: -moz-transform; + transition-property: transform; } + +.list-left-editing .item-left-editable .item-content, +.item-left-editing.item-left-editable .item-content { + -webkit-transform: translate3d(50px, 0, 0); + transform: translate3d(50px, 0, 0); } + +.item-remove-animate.ng-leave { + -webkit-transition-duration: 300ms; + transition-duration: 300ms; } + +.item-remove-animate.ng-leave .item-content, .item-remove-animate.ng-leave:last-of-type { + -webkit-transition-duration: 300ms; + transition-duration: 300ms; + -webkit-transition-timing-function: ease-in; + transition-timing-function: ease-in; + -webkit-transition-property: all; + transition-property: all; } + +.item-remove-animate.ng-leave.ng-leave-active .item-content { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0) !important; + transform: translate3d(-100%, 0, 0) !important; } + +.item-remove-animate.ng-leave.ng-leave-active:last-of-type { + opacity: 0; } + +.item-remove-animate.ng-leave.ng-leave-active ~ ion-item:not(.ng-leave) { + -webkit-transform: translate3d(0, -webkit-calc(-100% + 1px), 0); + transform: translate3d(0, calc(-100% + 1px), 0); + -webkit-transition-duration: 300ms; + transition-duration: 300ms; + -webkit-transition-timing-function: cubic-bezier(0.25, 0.81, 0.24, 1); + transition-timing-function: cubic-bezier(0.25, 0.81, 0.24, 1); + -webkit-transition-property: all; + transition-property: all; } + +.item-left-edit { + -webkit-transition: all ease-in-out 125ms; + transition: all ease-in-out 125ms; + position: absolute; + top: 0; + left: 0; + z-index: 0; + width: 50px; + height: 100%; + line-height: 100%; + display: none; + opacity: 0; + -webkit-transform: translate3d(-21px, 0, 0); + transform: translate3d(-21px, 0, 0); } + .item-left-edit .button { + height: 100%; } + .item-left-edit .button.icon { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + -moz-align-items: center; + align-items: center; + position: absolute; + top: 0; + height: 100%; } + .item-left-edit.visible { + display: block; } + .item-left-edit.visible.active { + opacity: 1; + -webkit-transform: translate3d(8px, 0, 0); + transform: translate3d(8px, 0, 0); } + +.list-left-editing .item-left-edit { + -webkit-transition-delay: 125ms; + transition-delay: 125ms; } + +.item-delete .button.icon { + color: #ef473a; + font-size: 24px; } + .item-delete .button.icon:hover { + opacity: .7; } + +.item-right-edit { + -webkit-transition: all ease-in-out 250ms; + transition: all ease-in-out 250ms; + position: absolute; + top: 0; + right: 0; + z-index: 3; + width: 75px; + height: 100%; + background: inherit; + padding-left: 20px; + display: block; + opacity: 0; + -webkit-transform: translate3d(75px, 0, 0); + transform: translate3d(75px, 0, 0); } + .item-right-edit .button { + min-width: 50px; + height: 100%; } + .item-right-edit .button.icon { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + -moz-align-items: center; + align-items: center; + position: absolute; + top: 0; + height: 100%; + font-size: 32px; } + .item-right-edit.visible { + display: block; } + .item-right-edit.visible.active { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); } + +.item-reorder .button.icon { + color: #444; + font-size: 32px; } + +.item-reordering { + position: absolute; + left: 0; + top: 0; + z-index: 9; + width: 100%; + box-shadow: 0px 0px 10px 0px #aaa; } + .item-reordering .item-reorder { + z-index: 9; } + +.item-placeholder { + opacity: 0.7; } + +/** + * The hidden right-side buttons that can be exposed under a list item + * with dragging. + */ +.item-options { + position: absolute; + top: 0; + right: 0; + z-index: 1; + height: 100%; } + .item-options .button { + height: 100%; + border: none; + border-radius: 0; + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -moz-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + -moz-align-items: center; + align-items: center; } + .item-options .button:before { + margin: 0 auto; } + +/** + * Lists + * -------------------------------------------------- + */ +.list { + position: relative; + padding-top: 1px; + padding-bottom: 1px; + padding-left: 0; + margin-bottom: 20px; } + +.list:last-child { + margin-bottom: 0px; } + .list:last-child.card { + margin-bottom: 40px; } + +/** + * List Header + * -------------------------------------------------- + */ +.list-header { + margin-top: 20px; + padding: 5px 15px; + background-color: transparent; + color: #222; + font-weight: bold; } + +.card.list .list-item { + padding-right: 1px; + padding-left: 1px; } + +/** + * Cards and Inset Lists + * -------------------------------------------------- + * A card and list-inset are close to the same thing, except a card as a box shadow. + */ +.card, +.list-inset { + overflow: hidden; + margin: 20px 10px; + border-radius: 2px; + background-color: #fff; } + +.card { + padding-top: 1px; + padding-bottom: 1px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); } + .card .item { + border-left: 0; + border-right: 0; } + .card .item:first-child { + border-top: 0; } + .card .item:last-child { + border-bottom: 0; } + +.padding .card, .padding .list-inset { + margin-left: 0; + margin-right: 0; } + +.card .item:first-child, +.list-inset .item:first-child, +.padding > .list .item:first-child { + border-top-left-radius: 2px; + border-top-right-radius: 2px; } + .card .item:first-child .item-content, + .list-inset .item:first-child .item-content, + .padding > .list .item:first-child .item-content { + border-top-left-radius: 2px; + border-top-right-radius: 2px; } + +.card .item:last-child, +.list-inset .item:last-child, +.padding > .list .item:last-child { + border-bottom-right-radius: 2px; + border-bottom-left-radius: 2px; } + .card .item:last-child .item-content, + .list-inset .item:last-child .item-content, + .padding > .list .item:last-child .item-content { + border-bottom-right-radius: 2px; + border-bottom-left-radius: 2px; } + +.card .item:last-child, +.list-inset .item:last-child { + margin-bottom: -1px; } + +.card .item, +.list-inset .item, +.padding > .list .item, +.padding-horizontal > .list .item { + margin-right: 0; + margin-left: 0; } + .card .item.item-input input, + .list-inset .item.item-input input, + .padding > .list .item.item-input input, + .padding-horizontal > .list .item.item-input input { + padding-right: 44px; } + +.padding-left > .list .item { + margin-left: 0; } + +.padding-right > .list .item { + margin-right: 0; } + +/** + * Badges + * -------------------------------------------------- + */ +.badge { + background-color: transparent; + color: #AAAAAA; + z-index: 1; + display: inline-block; + padding: 3px 8px; + min-width: 10px; + border-radius: 10px; + vertical-align: baseline; + text-align: center; + white-space: nowrap; + font-weight: bold; + font-size: 14px; + line-height: 16px; } + .badge:empty { + display: none; } + +.tabs .tab-item .badge.badge-light, +.badge.badge-light { + background-color: #fff; + color: #444; } + +.tabs .tab-item .badge.badge-stable, +.badge.badge-stable { + background-color: #f8f8f8; + color: #444; } + +.tabs .tab-item .badge.badge-positive, +.badge.badge-positive { + background-color: #387ef5; + color: #fff; } + +.tabs .tab-item .badge.badge-calm, +.badge.badge-calm { + background-color: #11c1f3; + color: #fff; } + +.tabs .tab-item .badge.badge-assertive, +.badge.badge-assertive { + background-color: #ef473a; + color: #fff; } + +.tabs .tab-item .badge.badge-balanced, +.badge.badge-balanced { + background-color: #33cd5f; + color: #fff; } + +.tabs .tab-item .badge.badge-energized, +.badge.badge-energized { + background-color: #ffc900; + color: #fff; } + +.tabs .tab-item .badge.badge-royal, +.badge.badge-royal { + background-color: #886aea; + color: #fff; } + +.tabs .tab-item .badge.badge-dark, +.badge.badge-dark { + background-color: #444; + color: #fff; } + +.button .badge { + position: relative; + top: -1px; } + +/** + * Slide Box + * -------------------------------------------------- + */ +.slider { + position: relative; + visibility: hidden; + overflow: hidden; } + +.slider-slides { + position: relative; + height: 100%; } + +.slider-slide { + position: relative; + display: block; + float: left; + width: 100%; + height: 100%; + vertical-align: top; } + +.slider-slide-image > img { + width: 100%; } + +.slider-pager { + position: absolute; + bottom: 20px; + z-index: 1; + width: 100%; + height: 15px; + text-align: center; } + .slider-pager .slider-pager-page { + display: inline-block; + margin: 0px 3px; + width: 15px; + color: #000; + text-decoration: none; + opacity: 0.3; } + .slider-pager .slider-pager-page.active { + -webkit-transition: opacity 0.4s ease-in; + transition: opacity 0.4s ease-in; + opacity: 1; } + +.slider-slide.ng-enter, .slider-slide.ng-leave, .slider-slide.ng-animate, +.slider-pager-page.ng-enter, +.slider-pager-page.ng-leave, +.slider-pager-page.ng-animate { + -webkit-transition: none !important; + transition: none !important; } + +.slider-slide.ng-animate, +.slider-pager-page.ng-animate { + -webkit-animation: none 0s; + animation: none 0s; } + +/** + * Swiper 3.2.7 + * Most modern mobile touch slider and framework with hardware accelerated transitions + * + * http://www.idangero.us/swiper/ + * + * Copyright 2015, Vladimir Kharlampidi + * The iDangero.us + * http://www.idangero.us/ + * + * Licensed under MIT + * + * Released on: December 7, 2015 + */ +.swiper-container { + margin: 0 auto; + position: relative; + overflow: hidden; + /* Fix of Webkit flickering */ + z-index: 1; } + +.swiper-container-no-flexbox .swiper-slide { + float: left; } + +.swiper-container-vertical > .swiper-wrapper { + -webkit-box-orient: vertical; + -moz-box-orient: vertical; + -ms-flex-direction: column; + -webkit-flex-direction: column; + flex-direction: column; } + +.swiper-wrapper { + position: relative; + width: 100%; + height: 100%; + z-index: 1; + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + -webkit-transition-property: -webkit-transform; + -moz-transition-property: -moz-transform; + -o-transition-property: -o-transform; + -ms-transition-property: -ms-transform; + transition-property: transform; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; } + +.swiper-container-android .swiper-slide, +.swiper-wrapper { + -webkit-transform: translate3d(0px, 0, 0); + -moz-transform: translate3d(0px, 0, 0); + -o-transform: translate(0px, 0px); + -ms-transform: translate3d(0px, 0, 0); + transform: translate3d(0px, 0, 0); } + +.swiper-container-multirow > .swiper-wrapper { + -webkit-box-lines: multiple; + -moz-box-lines: multiple; + -ms-flex-wrap: wrap; + -webkit-flex-wrap: wrap; + flex-wrap: wrap; } + +.swiper-container-free-mode > .swiper-wrapper { + -webkit-transition-timing-function: ease-out; + -moz-transition-timing-function: ease-out; + -ms-transition-timing-function: ease-out; + -o-transition-timing-function: ease-out; + transition-timing-function: ease-out; + margin: 0 auto; } + +.swiper-slide { + display: block; + -webkit-flex-shrink: 0; + -ms-flex: 0 0 auto; + flex-shrink: 0; + width: 100%; + height: 100%; + position: relative; } + +/* Auto Height */ +.swiper-container-autoheight, +.swiper-container-autoheight .swiper-slide { + height: auto; } + +.swiper-container-autoheight .swiper-wrapper { + -webkit-box-align: start; + -ms-flex-align: start; + -webkit-align-items: flex-start; + align-items: flex-start; + -webkit-transition-property: -webkit-transform, height; + -moz-transition-property: -moz-transform; + -o-transition-property: -o-transform; + -ms-transition-property: -ms-transform; + transition-property: transform, height; } + +/* a11y */ +.swiper-container .swiper-notification { + position: absolute; + left: 0; + top: 0; + pointer-events: none; + opacity: 0; + z-index: -1000; } + +/* IE10 Windows Phone 8 Fixes */ +.swiper-wp8-horizontal { + -ms-touch-action: pan-y; + touch-action: pan-y; } + +.swiper-wp8-vertical { + -ms-touch-action: pan-x; + touch-action: pan-x; } + +/* Arrows */ +.swiper-button-prev, +.swiper-button-next { + position: absolute; + top: 50%; + width: 27px; + height: 44px; + margin-top: -22px; + z-index: 10; + cursor: pointer; + -moz-background-size: 27px 44px; + -webkit-background-size: 27px 44px; + background-size: 27px 44px; + background-position: center; + background-repeat: no-repeat; } + +.swiper-button-prev.swiper-button-disabled, +.swiper-button-next.swiper-button-disabled { + opacity: 0.35; + cursor: auto; + pointer-events: none; } + +.swiper-button-prev, +.swiper-container-rtl .swiper-button-next { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M0%2C22L22%2C0l2.1%2C2.1L4.2%2C22l19.9%2C19.9L22%2C44L0%2C22L0%2C22L0%2C22z'%20fill%3D'%23007aff'%2F%3E%3C%2Fsvg%3E"); + left: 10px; + right: auto; } + +.swiper-button-prev.swiper-button-black, +.swiper-container-rtl .swiper-button-next.swiper-button-black { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M0%2C22L22%2C0l2.1%2C2.1L4.2%2C22l19.9%2C19.9L22%2C44L0%2C22L0%2C22L0%2C22z'%20fill%3D'%23000000'%2F%3E%3C%2Fsvg%3E"); } + +.swiper-button-prev.swiper-button-white, +.swiper-container-rtl .swiper-button-next.swiper-button-white { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M0%2C22L22%2C0l2.1%2C2.1L4.2%2C22l19.9%2C19.9L22%2C44L0%2C22L0%2C22L0%2C22z'%20fill%3D'%23ffffff'%2F%3E%3C%2Fsvg%3E"); } + +.swiper-button-next, +.swiper-container-rtl .swiper-button-prev { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M27%2C22L27%2C22L5%2C44l-2.1-2.1L22.8%2C22L2.9%2C2.1L5%2C0L27%2C22L27%2C22z'%20fill%3D'%23007aff'%2F%3E%3C%2Fsvg%3E"); + right: 10px; + left: auto; } + +.swiper-button-next.swiper-button-black, +.swiper-container-rtl .swiper-button-prev.swiper-button-black { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M27%2C22L27%2C22L5%2C44l-2.1-2.1L22.8%2C22L2.9%2C2.1L5%2C0L27%2C22L27%2C22z'%20fill%3D'%23000000'%2F%3E%3C%2Fsvg%3E"); } + +.swiper-button-next.swiper-button-white, +.swiper-container-rtl .swiper-button-prev.swiper-button-white { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M27%2C22L27%2C22L5%2C44l-2.1-2.1L22.8%2C22L2.9%2C2.1L5%2C0L27%2C22L27%2C22z'%20fill%3D'%23ffffff'%2F%3E%3C%2Fsvg%3E"); } + +/* Pagination Styles */ +.swiper-pagination { + position: absolute; + text-align: center; + -webkit-transition: 300ms; + -moz-transition: 300ms; + -o-transition: 300ms; + transition: 300ms; + -webkit-transform: translate3d(0, 0, 0); + -ms-transform: translate3d(0, 0, 0); + -o-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + z-index: 10; } + +.swiper-pagination.swiper-pagination-hidden { + opacity: 0; } + +.swiper-pagination-bullet { + width: 8px; + height: 8px; + display: inline-block; + border-radius: 100%; + background: #000; + opacity: 0.2; } + +button.swiper-pagination-bullet { + border: none; + margin: 0; + padding: 0; + box-shadow: none; + -moz-appearance: none; + -ms-appearance: none; + -webkit-appearance: none; + appearance: none; } + +.swiper-pagination-clickable .swiper-pagination-bullet { + cursor: pointer; } + +.swiper-pagination-white .swiper-pagination-bullet { + background: #fff; } + +.swiper-pagination-bullet-active { + opacity: 1; } + +.swiper-pagination-white .swiper-pagination-bullet-active { + background: #fff; } + +.swiper-pagination-black .swiper-pagination-bullet-active { + background: #000; } + +.swiper-container-vertical > .swiper-pagination { + right: 10px; + top: 50%; + -webkit-transform: translate3d(0px, -50%, 0); + -moz-transform: translate3d(0px, -50%, 0); + -o-transform: translate(0px, -50%); + -ms-transform: translate3d(0px, -50%, 0); + transform: translate3d(0px, -50%, 0); } + +.swiper-container-vertical > .swiper-pagination .swiper-pagination-bullet { + margin: 5px 0; + display: block; } + +.swiper-container-horizontal > .swiper-pagination { + bottom: 10px; + left: 0; + width: 100%; } + +.swiper-container-horizontal > .swiper-pagination .swiper-pagination-bullet { + margin: 0 5px; } + +/* 3D Container */ +.swiper-container-3d { + -webkit-perspective: 1200px; + -moz-perspective: 1200px; + -o-perspective: 1200px; + perspective: 1200px; } + +.swiper-container-3d .swiper-wrapper, +.swiper-container-3d .swiper-slide, +.swiper-container-3d .swiper-slide-shadow-left, +.swiper-container-3d .swiper-slide-shadow-right, +.swiper-container-3d .swiper-slide-shadow-top, +.swiper-container-3d .swiper-slide-shadow-bottom, +.swiper-container-3d .swiper-cube-shadow { + -webkit-transform-style: preserve-3d; + -moz-transform-style: preserve-3d; + -ms-transform-style: preserve-3d; + transform-style: preserve-3d; } + +.swiper-container-3d .swiper-slide-shadow-left, +.swiper-container-3d .swiper-slide-shadow-right, +.swiper-container-3d .swiper-slide-shadow-top, +.swiper-container-3d .swiper-slide-shadow-bottom { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 10; } + +.swiper-container-3d .swiper-slide-shadow-left { + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, 0.5)), to(transparent)); + /* Safari 4+, Chrome */ + background-image: -webkit-linear-gradient(right, rgba(0, 0, 0, 0.5), transparent); + /* Chrome 10+, Safari 5.1+, iOS 5+ */ + background-image: -moz-linear-gradient(right, rgba(0, 0, 0, 0.5), transparent); + /* Firefox 3.6-15 */ + background-image: -o-linear-gradient(right, rgba(0, 0, 0, 0.5), transparent); + /* Opera 11.10-12.00 */ + background-image: linear-gradient(to left, rgba(0, 0, 0, 0.5), transparent); + /* Firefox 16+, IE10, Opera 12.50+ */ } + +.swiper-container-3d .swiper-slide-shadow-right { + background-image: -webkit-gradient(linear, right top, left top, from(rgba(0, 0, 0, 0.5)), to(transparent)); + /* Safari 4+, Chrome */ + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5), transparent); + /* Chrome 10+, Safari 5.1+, iOS 5+ */ + background-image: -moz-linear-gradient(left, rgba(0, 0, 0, 0.5), transparent); + /* Firefox 3.6-15 */ + background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5), transparent); + /* Opera 11.10-12.00 */ + background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5), transparent); + /* Firefox 16+, IE10, Opera 12.50+ */ } + +.swiper-container-3d .swiper-slide-shadow-top { + background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0.5)), to(transparent)); + /* Safari 4+, Chrome */ + background-image: -webkit-linear-gradient(bottom, rgba(0, 0, 0, 0.5), transparent); + /* Chrome 10+, Safari 5.1+, iOS 5+ */ + background-image: -moz-linear-gradient(bottom, rgba(0, 0, 0, 0.5), transparent); + /* Firefox 3.6-15 */ + background-image: -o-linear-gradient(bottom, rgba(0, 0, 0, 0.5), transparent); + /* Opera 11.10-12.00 */ + background-image: linear-gradient(to top, rgba(0, 0, 0, 0.5), transparent); + /* Firefox 16+, IE10, Opera 12.50+ */ } + +.swiper-container-3d .swiper-slide-shadow-bottom { + background-image: -webkit-gradient(linear, left bottom, left top, from(rgba(0, 0, 0, 0.5)), to(transparent)); + /* Safari 4+, Chrome */ + background-image: -webkit-linear-gradient(top, rgba(0, 0, 0, 0.5), transparent); + /* Chrome 10+, Safari 5.1+, iOS 5+ */ + background-image: -moz-linear-gradient(top, rgba(0, 0, 0, 0.5), transparent); + /* Firefox 3.6-15 */ + background-image: -o-linear-gradient(top, rgba(0, 0, 0, 0.5), transparent); + /* Opera 11.10-12.00 */ + background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), transparent); + /* Firefox 16+, IE10, Opera 12.50+ */ } + +/* Coverflow */ +.swiper-container-coverflow .swiper-wrapper { + /* Windows 8 IE 10 fix */ + -ms-perspective: 1200px; } + +/* Fade */ +.swiper-container-fade.swiper-container-free-mode .swiper-slide { + -webkit-transition-timing-function: ease-out; + -moz-transition-timing-function: ease-out; + -ms-transition-timing-function: ease-out; + -o-transition-timing-function: ease-out; + transition-timing-function: ease-out; } + +.swiper-container-fade .swiper-slide { + pointer-events: none; } + +.swiper-container-fade .swiper-slide .swiper-slide { + pointer-events: none; } + +.swiper-container-fade .swiper-slide-active, +.swiper-container-fade .swiper-slide-active .swiper-slide-active { + pointer-events: auto; } + +/* Cube */ +.swiper-container-cube { + overflow: visible; } + +.swiper-container-cube .swiper-slide { + pointer-events: none; + visibility: hidden; + -webkit-transform-origin: 0 0; + -moz-transform-origin: 0 0; + -ms-transform-origin: 0 0; + transform-origin: 0 0; + -webkit-backface-visibility: hidden; + -moz-backface-visibility: hidden; + -ms-backface-visibility: hidden; + backface-visibility: hidden; + width: 100%; + height: 100%; + z-index: 1; } + +.swiper-container-cube.swiper-container-rtl .swiper-slide { + -webkit-transform-origin: 100% 0; + -moz-transform-origin: 100% 0; + -ms-transform-origin: 100% 0; + transform-origin: 100% 0; } + +.swiper-container-cube .swiper-slide-active, +.swiper-container-cube .swiper-slide-next, +.swiper-container-cube .swiper-slide-prev, +.swiper-container-cube .swiper-slide-next + .swiper-slide { + pointer-events: auto; + visibility: visible; } + +.swiper-container-cube .swiper-slide-shadow-top, +.swiper-container-cube .swiper-slide-shadow-bottom, +.swiper-container-cube .swiper-slide-shadow-left, +.swiper-container-cube .swiper-slide-shadow-right { + z-index: 0; + -webkit-backface-visibility: hidden; + -moz-backface-visibility: hidden; + -ms-backface-visibility: hidden; + backface-visibility: hidden; } + +.swiper-container-cube .swiper-cube-shadow { + position: absolute; + left: 0; + bottom: 0px; + width: 100%; + height: 100%; + background: #000; + opacity: 0.6; + -webkit-filter: blur(50px); + filter: blur(50px); + z-index: 0; } + +/* Scrollbar */ +.swiper-scrollbar { + border-radius: 10px; + position: relative; + -ms-touch-action: none; + background: rgba(0, 0, 0, 0.1); } + +.swiper-container-horizontal > .swiper-scrollbar { + position: absolute; + left: 1%; + bottom: 3px; + z-index: 50; + height: 5px; + width: 98%; } + +.swiper-container-vertical > .swiper-scrollbar { + position: absolute; + right: 3px; + top: 1%; + z-index: 50; + width: 5px; + height: 98%; } + +.swiper-scrollbar-drag { + height: 100%; + width: 100%; + position: relative; + background: rgba(0, 0, 0, 0.5); + border-radius: 10px; + left: 0; + top: 0; } + +.swiper-scrollbar-cursor-drag { + cursor: move; } + +/* Preloader */ +.swiper-lazy-preloader { + width: 42px; + height: 42px; + position: absolute; + left: 50%; + top: 50%; + margin-left: -21px; + margin-top: -21px; + z-index: 10; + -webkit-transform-origin: 50%; + -moz-transform-origin: 50%; + transform-origin: 50%; + -webkit-animation: swiper-preloader-spin 1s steps(12, end) infinite; + -moz-animation: swiper-preloader-spin 1s steps(12, end) infinite; + animation: swiper-preloader-spin 1s steps(12, end) infinite; } + +.swiper-lazy-preloader:after { + display: block; + content: ""; + width: 100%; + height: 100%; + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20viewBox%3D'0%200%20120%20120'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20xmlns%3Axlink%3D'http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink'%3E%3Cdefs%3E%3Cline%20id%3D'l'%20x1%3D'60'%20x2%3D'60'%20y1%3D'7'%20y2%3D'27'%20stroke%3D'%236c6c6c'%20stroke-width%3D'11'%20stroke-linecap%3D'round'%2F%3E%3C%2Fdefs%3E%3Cg%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(30%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(60%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(90%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(120%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(150%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.37'%20transform%3D'rotate(180%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.46'%20transform%3D'rotate(210%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.56'%20transform%3D'rotate(240%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.66'%20transform%3D'rotate(270%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.75'%20transform%3D'rotate(300%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.85'%20transform%3D'rotate(330%2060%2C60)'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E"); + background-position: 50%; + -webkit-background-size: 100%; + background-size: 100%; + background-repeat: no-repeat; } + +.swiper-lazy-preloader-white:after { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20viewBox%3D'0%200%20120%20120'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20xmlns%3Axlink%3D'http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink'%3E%3Cdefs%3E%3Cline%20id%3D'l'%20x1%3D'60'%20x2%3D'60'%20y1%3D'7'%20y2%3D'27'%20stroke%3D'%23fff'%20stroke-width%3D'11'%20stroke-linecap%3D'round'%2F%3E%3C%2Fdefs%3E%3Cg%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(30%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(60%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(90%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(120%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(150%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.37'%20transform%3D'rotate(180%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.46'%20transform%3D'rotate(210%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.56'%20transform%3D'rotate(240%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.66'%20transform%3D'rotate(270%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.75'%20transform%3D'rotate(300%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.85'%20transform%3D'rotate(330%2060%2C60)'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } + +@-webkit-keyframes swiper-preloader-spin { + 100% { + -webkit-transform: rotate(360deg); } } + +@keyframes swiper-preloader-spin { + 100% { + transform: rotate(360deg); } } + +ion-slides { + width: 100%; + height: 100%; + display: block; } + +.slide-zoom { + display: block; + width: 100%; + text-align: center; } + +.swiper-container { + width: 100%; + height: 100%; + padding: 0; + overflow: hidden; } + +.swiper-wrapper { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + padding: 0; } + +.swiper-slide { + width: 100%; + height: 100%; + box-sizing: border-box; + /* Center slide text vertically */ } + .swiper-slide img { + width: auto; + height: auto; + max-width: 100%; + max-height: 100%; } + +.scroll-refresher { + position: absolute; + top: -60px; + right: 0; + left: 0; + overflow: hidden; + margin: auto; + height: 60px; } + .scroll-refresher .ionic-refresher-content { + position: absolute; + bottom: 15px; + left: 0; + width: 100%; + color: #666666; + text-align: center; + font-size: 30px; } + .scroll-refresher .ionic-refresher-content .text-refreshing, + .scroll-refresher .ionic-refresher-content .text-pulling { + font-size: 16px; + line-height: 16px; } + .scroll-refresher .ionic-refresher-content.ionic-refresher-with-text { + bottom: 10px; } + .scroll-refresher .icon-refreshing, + .scroll-refresher .icon-pulling { + width: 100%; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; } + .scroll-refresher .icon-pulling { + -webkit-animation-name: refresh-spin-back; + animation-name: refresh-spin-back; + -webkit-animation-duration: 200ms; + animation-duration: 200ms; + -webkit-animation-timing-function: linear; + animation-timing-function: linear; + -webkit-animation-fill-mode: none; + animation-fill-mode: none; + -webkit-transform: translate3d(0, 0, 0) rotate(0deg); + transform: translate3d(0, 0, 0) rotate(0deg); } + .scroll-refresher .icon-refreshing, + .scroll-refresher .text-refreshing { + display: none; } + .scroll-refresher .icon-refreshing { + -webkit-animation-duration: 1.5s; + animation-duration: 1.5s; } + .scroll-refresher.active .icon-pulling:not(.pulling-rotation-disabled) { + -webkit-animation-name: refresh-spin; + animation-name: refresh-spin; + -webkit-transform: translate3d(0, 0, 0) rotate(-180deg); + transform: translate3d(0, 0, 0) rotate(-180deg); } + .scroll-refresher.active.refreshing { + -webkit-transition: -webkit-transform 0.2s; + transition: -webkit-transform 0.2s; + -webkit-transition: transform 0.2s; + transition: transform 0.2s; + -webkit-transform: scale(1, 1); + transform: scale(1, 1); } + .scroll-refresher.active.refreshing .icon-pulling, + .scroll-refresher.active.refreshing .text-pulling { + display: none; } + .scroll-refresher.active.refreshing .icon-refreshing, + .scroll-refresher.active.refreshing .text-refreshing { + display: block; } + .scroll-refresher.active.refreshing.refreshing-tail { + -webkit-transform: scale(0, 0); + transform: scale(0, 0); } + +.overflow-scroll > .scroll { + -webkit-overflow-scrolling: touch; + width: 100%; } + .overflow-scroll > .scroll.overscroll { + position: fixed; + right: 0; + left: 0; } + +.overflow-scroll.padding > .scroll.overscroll { + padding: 10px; } + +@-webkit-keyframes refresh-spin { + 0% { + -webkit-transform: translate3d(0, 0, 0) rotate(0); } + 100% { + -webkit-transform: translate3d(0, 0, 0) rotate(180deg); } } + +@keyframes refresh-spin { + 0% { + transform: translate3d(0, 0, 0) rotate(0); } + 100% { + transform: translate3d(0, 0, 0) rotate(180deg); } } + +@-webkit-keyframes refresh-spin-back { + 0% { + -webkit-transform: translate3d(0, 0, 0) rotate(180deg); } + 100% { + -webkit-transform: translate3d(0, 0, 0) rotate(0); } } + +@keyframes refresh-spin-back { + 0% { + transform: translate3d(0, 0, 0) rotate(180deg); } + 100% { + transform: translate3d(0, 0, 0) rotate(0); } } + +/** + * Spinners + * -------------------------------------------------- + */ +.spinner { + stroke: #444; + fill: #444; } + .spinner svg { + width: 28px; + height: 28px; } + .spinner.spinner-light { + stroke: #fff; + fill: #fff; } + .spinner.spinner-stable { + stroke: #f8f8f8; + fill: #f8f8f8; } + .spinner.spinner-positive { + stroke: #387ef5; + fill: #387ef5; } + .spinner.spinner-calm { + stroke: #11c1f3; + fill: #11c1f3; } + .spinner.spinner-balanced { + stroke: #33cd5f; + fill: #33cd5f; } + .spinner.spinner-assertive { + stroke: #ef473a; + fill: #ef473a; } + .spinner.spinner-energized { + stroke: #ffc900; + fill: #ffc900; } + .spinner.spinner-royal { + stroke: #886aea; + fill: #886aea; } + .spinner.spinner-dark { + stroke: #444; + fill: #444; } + +.spinner-android { + stroke: #4b8bf4; } + +.spinner-ios, +.spinner-ios-small { + stroke: #69717d; } + +.spinner-spiral .stop1 { + stop-color: #fff; + stop-opacity: 0; } + +.spinner-spiral.spinner-light .stop1 { + stop-color: #444; } + +.spinner-spiral.spinner-light .stop2 { + stop-color: #fff; } + +.spinner-spiral.spinner-stable .stop2 { + stop-color: #f8f8f8; } + +.spinner-spiral.spinner-positive .stop2 { + stop-color: #387ef5; } + +.spinner-spiral.spinner-calm .stop2 { + stop-color: #11c1f3; } + +.spinner-spiral.spinner-balanced .stop2 { + stop-color: #33cd5f; } + +.spinner-spiral.spinner-assertive .stop2 { + stop-color: #ef473a; } + +.spinner-spiral.spinner-energized .stop2 { + stop-color: #ffc900; } + +.spinner-spiral.spinner-royal .stop2 { + stop-color: #886aea; } + +.spinner-spiral.spinner-dark .stop2 { + stop-color: #444; } + +/** + * Forms + * -------------------------------------------------- + */ +form { + margin: 0 0 1.42857; } + +legend { + display: block; + margin-bottom: 1.42857; + padding: 0; + width: 100%; + border: 1px solid #ddd; + color: #444; + font-size: 21px; + line-height: 2.85714; } + legend small { + color: #f8f8f8; + font-size: 1.07143; } + +label, +input, +button, +select, +textarea { + font-weight: normal; + font-size: 14px; + line-height: 1.42857; } + +input, +button, +select, +textarea { + font-family: "-apple-system", "Helvetica Neue", "Roboto", "Segoe UI", sans-serif; } + +.item-input { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + -moz-align-items: center; + align-items: center; + position: relative; + overflow: hidden; + padding: 6px 0 5px 16px; } + .item-input input { + -webkit-border-radius: 0; + border-radius: 0; + -webkit-box-flex: 1; + -webkit-flex: 1 220px; + -moz-box-flex: 1; + -moz-flex: 1 220px; + -ms-flex: 1 220px; + flex: 1 220px; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + margin: 0; + padding-right: 24px; + background-color: transparent; } + .item-input .button .icon { + -webkit-box-flex: 0; + -webkit-flex: 0 0 24px; + -moz-box-flex: 0; + -moz-flex: 0 0 24px; + -ms-flex: 0 0 24px; + flex: 0 0 24px; + position: static; + display: inline-block; + height: auto; + text-align: center; + font-size: 16px; } + .item-input .button-bar { + -webkit-border-radius: 0; + border-radius: 0; + -webkit-box-flex: 1; + -webkit-flex: 1 0 220px; + -moz-box-flex: 1; + -moz-flex: 1 0 220px; + -ms-flex: 1 0 220px; + flex: 1 0 220px; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; } + .item-input .icon { + min-width: 14px; } + +.platform-windowsphone .item-input input { + flex-shrink: 1; } + +.item-input-inset { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + -moz-align-items: center; + align-items: center; + position: relative; + overflow: hidden; + padding: 10.66667px; } + +.item-input-wrapper { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-flex: 1; + -webkit-flex: 1 0; + -moz-box-flex: 1; + -moz-flex: 1 0; + -ms-flex: 1 0; + flex: 1 0; + -webkit-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + -moz-align-items: center; + align-items: center; + -webkit-border-radius: 4px; + border-radius: 4px; + padding-right: 8px; + padding-left: 8px; + background: #eee; } + +.item-input-inset .item-input-wrapper input { + padding-left: 4px; + height: 29px; + background: transparent; + line-height: 18px; } + +.item-input-wrapper ~ .button { + margin-left: 10.66667px; } + +.input-label { + display: table; + padding: 7px 10px 7px 0px; + max-width: 200px; + width: 35%; + color: #444; + font-size: 16px; } + +.placeholder-icon { + color: #aaa; } + .placeholder-icon:first-child { + padding-right: 6px; } + .placeholder-icon:last-child { + padding-left: 6px; } + +.item-stacked-label { + display: block; + background-color: transparent; + box-shadow: none; } + .item-stacked-label .input-label, .item-stacked-label .icon { + display: inline-block; + padding: 4px 0 0 0px; + vertical-align: middle; } + +.item-stacked-label input, +.item-stacked-label textarea { + -webkit-border-radius: 2px; + border-radius: 2px; + padding: 4px 8px 3px 0; + border: none; + background-color: #fff; } + +.item-stacked-label input { + overflow: hidden; + height: 46px; } + +.item-select.item-stacked-label select { + position: relative; + padding: 0px; + max-width: 90%; + direction: ltr; + white-space: pre-wrap; + margin: -3px; } + +.item-floating-label { + display: block; + background-color: transparent; + box-shadow: none; } + .item-floating-label .input-label { + position: relative; + padding: 5px 0 0 0; + opacity: 0; + top: 10px; + -webkit-transition: opacity 0.15s ease-in, top 0.2s linear; + transition: opacity 0.15s ease-in, top 0.2s linear; } + .item-floating-label .input-label.has-input { + opacity: 1; + top: 0; + -webkit-transition: opacity 0.15s ease-in, top 0.2s linear; + transition: opacity 0.15s ease-in, top 0.2s linear; } + +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"] { + display: block; + padding-top: 2px; + padding-left: 0; + height: 34px; + color: #111; + vertical-align: middle; + font-size: 14px; + line-height: 16px; } + +.platform-ios input[type="datetime-local"], +.platform-ios input[type="date"], +.platform-ios input[type="month"], +.platform-ios input[type="time"], +.platform-ios input[type="week"], +.platform-android input[type="datetime-local"], +.platform-android input[type="date"], +.platform-android input[type="month"], +.platform-android input[type="time"], +.platform-android input[type="week"] { + padding-top: 8px; } + +.item-input input, +.item-input textarea { + width: 100%; } + +textarea { + padding-left: 0; } + textarea::-moz-placeholder { + color: #aaaaaa; } + textarea:-ms-input-placeholder { + color: #aaaaaa; } + textarea::-webkit-input-placeholder { + color: #aaaaaa; + text-indent: -3px; } + +textarea { + height: auto; } + +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"] { + border: 0; } + +input[type="radio"], +input[type="checkbox"] { + margin: 0; + line-height: normal; } + +.item-input input[type="file"], +.item-input input[type="image"], +.item-input input[type="submit"], +.item-input input[type="reset"], +.item-input input[type="button"], +.item-input input[type="radio"], +.item-input input[type="checkbox"] { + width: auto; } + +input[type="file"] { + line-height: 34px; } + +.previous-input-focus, +.cloned-text-input + input, +.cloned-text-input + textarea { + position: absolute !important; + left: -9999px; + width: 200px; } + +input::-moz-placeholder, +textarea::-moz-placeholder { + color: #aaaaaa; } + +input:-ms-input-placeholder, +textarea:-ms-input-placeholder { + color: #aaaaaa; } + +input::-webkit-input-placeholder, +textarea::-webkit-input-placeholder { + color: #aaaaaa; + text-indent: 0; } + +input[disabled], +select[disabled], +textarea[disabled], +input[readonly]:not(.cloned-text-input), +textarea[readonly]:not(.cloned-text-input), +select[readonly] { + background-color: #f8f8f8; + cursor: not-allowed; } + +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"][readonly], +input[type="checkbox"][readonly] { + background-color: transparent; } + +/** + * Checkbox + * -------------------------------------------------- + */ +.checkbox { + position: relative; + display: inline-block; + padding: 7px 7px; + cursor: pointer; } + .checkbox input:before, + .checkbox .checkbox-icon:before { + border-color: #ddd; } + .checkbox input:checked:before, + .checkbox input:checked + .checkbox-icon:before { + background: #387ef5; + border-color: #387ef5; } + +.checkbox-light input:before, +.checkbox-light .checkbox-icon:before { + border-color: #ddd; } + +.checkbox-light input:checked:before, +.checkbox-light input:checked + .checkbox-icon:before { + background: #ddd; + border-color: #ddd; } + +.checkbox-stable input:before, +.checkbox-stable .checkbox-icon:before { + border-color: #b2b2b2; } + +.checkbox-stable input:checked:before, +.checkbox-stable input:checked + .checkbox-icon:before { + background: #b2b2b2; + border-color: #b2b2b2; } + +.checkbox-positive input:before, +.checkbox-positive .checkbox-icon:before { + border-color: #387ef5; } + +.checkbox-positive input:checked:before, +.checkbox-positive input:checked + .checkbox-icon:before { + background: #387ef5; + border-color: #387ef5; } + +.checkbox-calm input:before, +.checkbox-calm .checkbox-icon:before { + border-color: #11c1f3; } + +.checkbox-calm input:checked:before, +.checkbox-calm input:checked + .checkbox-icon:before { + background: #11c1f3; + border-color: #11c1f3; } + +.checkbox-assertive input:before, +.checkbox-assertive .checkbox-icon:before { + border-color: #ef473a; } + +.checkbox-assertive input:checked:before, +.checkbox-assertive input:checked + .checkbox-icon:before { + background: #ef473a; + border-color: #ef473a; } + +.checkbox-balanced input:before, +.checkbox-balanced .checkbox-icon:before { + border-color: #33cd5f; } + +.checkbox-balanced input:checked:before, +.checkbox-balanced input:checked + .checkbox-icon:before { + background: #33cd5f; + border-color: #33cd5f; } + +.checkbox-energized input:before, +.checkbox-energized .checkbox-icon:before { + border-color: #ffc900; } + +.checkbox-energized input:checked:before, +.checkbox-energized input:checked + .checkbox-icon:before { + background: #ffc900; + border-color: #ffc900; } + +.checkbox-royal input:before, +.checkbox-royal .checkbox-icon:before { + border-color: #886aea; } + +.checkbox-royal input:checked:before, +.checkbox-royal input:checked + .checkbox-icon:before { + background: #886aea; + border-color: #886aea; } + +.checkbox-dark input:before, +.checkbox-dark .checkbox-icon:before { + border-color: #444; } + +.checkbox-dark input:checked:before, +.checkbox-dark input:checked + .checkbox-icon:before { + background: #444; + border-color: #444; } + +.checkbox input:disabled:before, +.checkbox input:disabled + .checkbox-icon:before { + border-color: #ddd; } + +.checkbox input:disabled:checked:before, +.checkbox input:disabled:checked + .checkbox-icon:before { + background: #ddd; } + +.checkbox.checkbox-input-hidden input { + display: none !important; } + +.checkbox input, +.checkbox-icon { + position: relative; + width: 28px; + height: 28px; + display: block; + border: 0; + background: transparent; + cursor: pointer; + -webkit-appearance: none; } + .checkbox input:before, + .checkbox-icon:before { + display: table; + width: 100%; + height: 100%; + border-width: 1px; + border-style: solid; + border-radius: 28px; + background: #fff; + content: ' '; + -webkit-transition: background-color 20ms ease-in-out; + transition: background-color 20ms ease-in-out; } + +.checkbox input:checked:before, +input:checked + .checkbox-icon:before { + border-width: 2px; } + +.checkbox input:after, +.checkbox-icon:after { + -webkit-transition: opacity 0.05s ease-in-out; + transition: opacity 0.05s ease-in-out; + -webkit-transform: rotate(-45deg); + transform: rotate(-45deg); + position: absolute; + top: 33%; + left: 25%; + display: table; + width: 14px; + height: 6px; + border: 1px solid #fff; + border-top: 0; + border-right: 0; + content: ' '; + opacity: 0; } + +.platform-android .checkbox-platform input:before, +.platform-android .checkbox-platform .checkbox-icon:before, +.checkbox-square input:before, +.checkbox-square .checkbox-icon:before { + border-radius: 2px; + width: 72%; + height: 72%; + margin-top: 14%; + margin-left: 14%; + border-width: 2px; } + +.platform-android .checkbox-platform input:after, +.platform-android .checkbox-platform .checkbox-icon:after, +.checkbox-square input:after, +.checkbox-square .checkbox-icon:after { + border-width: 2px; + top: 19%; + left: 25%; + width: 13px; + height: 7px; } + +.platform-android .item-checkbox-right .checkbox-square .checkbox-icon::after { + top: 31%; } + +.grade-c .checkbox input:after, +.grade-c .checkbox-icon:after { + -webkit-transform: rotate(0); + transform: rotate(0); + top: 3px; + left: 4px; + border: none; + color: #fff; + content: '\2713'; + font-weight: bold; + font-size: 20px; } + +.checkbox input:checked:after, +input:checked + .checkbox-icon:after { + opacity: 1; } + +.item-checkbox { + padding-left: 60px; } + .item-checkbox.active { + box-shadow: none; } + +.item-checkbox .checkbox { + position: absolute; + top: 50%; + right: 8px; + left: 8px; + z-index: 3; + margin-top: -21px; } + +.item-checkbox.item-checkbox-right { + padding-right: 60px; + padding-left: 16px; } + +.item-checkbox-right .checkbox input, +.item-checkbox-right .checkbox-icon { + float: right; } + +/** + * Toggle + * -------------------------------------------------- + */ +.item-toggle { + pointer-events: none; } + +.toggle { + position: relative; + display: inline-block; + pointer-events: auto; + margin: -5px; + padding: 5px; } + .toggle input:checked + .track { + border-color: #4cd964; + background-color: #4cd964; } + .toggle.dragging .handle { + background-color: #f2f2f2 !important; } + +.toggle.toggle-light input:checked + .track { + border-color: #ddd; + background-color: #ddd; } + +.toggle.toggle-stable input:checked + .track { + border-color: #b2b2b2; + background-color: #b2b2b2; } + +.toggle.toggle-positive input:checked + .track { + border-color: #387ef5; + background-color: #387ef5; } + +.toggle.toggle-calm input:checked + .track { + border-color: #11c1f3; + background-color: #11c1f3; } + +.toggle.toggle-assertive input:checked + .track { + border-color: #ef473a; + background-color: #ef473a; } + +.toggle.toggle-balanced input:checked + .track { + border-color: #33cd5f; + background-color: #33cd5f; } + +.toggle.toggle-energized input:checked + .track { + border-color: #ffc900; + background-color: #ffc900; } + +.toggle.toggle-royal input:checked + .track { + border-color: #886aea; + background-color: #886aea; } + +.toggle.toggle-dark input:checked + .track { + border-color: #444; + background-color: #444; } + +.toggle input { + display: none; } + +/* the track appearance when the toggle is "off" */ +.toggle .track { + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; + -webkit-transition-duration: 0.3s; + transition-duration: 0.3s; + -webkit-transition-property: background-color, border; + transition-property: background-color, border; + display: inline-block; + box-sizing: border-box; + width: 51px; + height: 31px; + border: solid 2px #e6e6e6; + border-radius: 20px; + background-color: #fff; + content: ' '; + cursor: pointer; + pointer-events: none; } + +/* Fix to avoid background color bleeding */ +/* (occurred on (at least) Android 4.2, Asus MeMO Pad HD7 ME173X) */ +.platform-android4_2 .toggle .track { + -webkit-background-clip: padding-box; } + +/* the handle (circle) thats inside the toggle's track area */ +/* also the handle's appearance when it is "off" */ +.toggle .handle { + -webkit-transition: 0.3s cubic-bezier(0, 1.1, 1, 1.1); + transition: 0.3s cubic-bezier(0, 1.1, 1, 1.1); + -webkit-transition-property: background-color, transform; + transition-property: background-color, transform; + position: absolute; + display: block; + width: 27px; + height: 27px; + border-radius: 27px; + background-color: #fff; + top: 7px; + left: 7px; + box-shadow: 0 2px 7px rgba(0, 0, 0, 0.35), 0 1px 1px rgba(0, 0, 0, 0.15); } + .toggle .handle:before { + position: absolute; + top: -4px; + left: -21.5px; + padding: 18.5px 34px; + content: " "; } + +.toggle input:checked + .track .handle { + -webkit-transform: translate3d(20px, 0, 0); + transform: translate3d(20px, 0, 0); + background-color: #fff; } + +.item-toggle.active { + box-shadow: none; } + +.item-toggle, +.item-toggle.item-complex .item-content { + padding-right: 99px; } + +.item-toggle.item-complex { + padding-right: 0; } + +.item-toggle .toggle { + position: absolute; + top: 10px; + right: 16px; + z-index: 3; } + +.toggle input:disabled + .track { + opacity: .6; } + +.toggle-small .track { + border: 0; + width: 34px; + height: 15px; + background: #9e9e9e; } + +.toggle-small input:checked + .track { + background: rgba(0, 150, 137, 0.5); } + +.toggle-small .handle { + top: 2px; + left: 4px; + width: 21px; + height: 21px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.25); } + +.toggle-small input:checked + .track .handle { + -webkit-transform: translate3d(16px, 0, 0); + transform: translate3d(16px, 0, 0); + background: #009689; } + +.toggle-small.item-toggle .toggle { + top: 19px; } + +.toggle-small .toggle-light input:checked + .track { + background-color: rgba(221, 221, 221, 0.5); } + +.toggle-small .toggle-light input:checked + .track .handle { + background-color: #ddd; } + +.toggle-small .toggle-stable input:checked + .track { + background-color: rgba(178, 178, 178, 0.5); } + +.toggle-small .toggle-stable input:checked + .track .handle { + background-color: #b2b2b2; } + +.toggle-small .toggle-positive input:checked + .track { + background-color: rgba(56, 126, 245, 0.5); } + +.toggle-small .toggle-positive input:checked + .track .handle { + background-color: #387ef5; } + +.toggle-small .toggle-calm input:checked + .track { + background-color: rgba(17, 193, 243, 0.5); } + +.toggle-small .toggle-calm input:checked + .track .handle { + background-color: #11c1f3; } + +.toggle-small .toggle-assertive input:checked + .track { + background-color: rgba(239, 71, 58, 0.5); } + +.toggle-small .toggle-assertive input:checked + .track .handle { + background-color: #ef473a; } + +.toggle-small .toggle-balanced input:checked + .track { + background-color: rgba(51, 205, 95, 0.5); } + +.toggle-small .toggle-balanced input:checked + .track .handle { + background-color: #33cd5f; } + +.toggle-small .toggle-energized input:checked + .track { + background-color: rgba(255, 201, 0, 0.5); } + +.toggle-small .toggle-energized input:checked + .track .handle { + background-color: #ffc900; } + +.toggle-small .toggle-royal input:checked + .track { + background-color: rgba(136, 106, 234, 0.5); } + +.toggle-small .toggle-royal input:checked + .track .handle { + background-color: #886aea; } + +.toggle-small .toggle-dark input:checked + .track { + background-color: rgba(68, 68, 68, 0.5); } + +.toggle-small .toggle-dark input:checked + .track .handle { + background-color: #444; } + +/** + * Radio Button Inputs + * -------------------------------------------------- + */ +.item-radio { + padding: 0; } + .item-radio:hover { + cursor: pointer; } + +.item-radio .item-content { + /* give some room to the right for the checkmark icon */ + padding-right: 64px; } + +.item-radio .radio-icon { + /* checkmark icon will be hidden by default */ + position: absolute; + top: 0; + right: 0; + z-index: 3; + visibility: hidden; + padding: 14px; + height: 100%; + font-size: 24px; } + +.item-radio input { + /* hide any radio button inputs elements (the ugly circles) */ + position: absolute; + left: -9999px; } + .item-radio input:checked + .radio-content .item-content { + /* style the item content when its checked */ + background: #f7f7f7; } + .item-radio input:checked + .radio-content .radio-icon { + /* show the checkmark icon when its checked */ + visibility: visible; } + +/** + * Range + * -------------------------------------------------- + */ +.range input { + display: inline-block; + overflow: hidden; + margin-top: 5px; + margin-bottom: 5px; + padding-right: 2px; + padding-left: 1px; + width: auto; + height: 43px; + outline: none; + background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ccc), color-stop(100%, #ccc)); + background: linear-gradient(to right, #ccc 0%, #ccc 100%); + background-position: center; + background-size: 99% 2px; + background-repeat: no-repeat; + -webkit-appearance: none; + /* + &::-ms-track{ + background: transparent; + border-color: transparent; + border-width: 11px 0 16px; + color:transparent; + margin-top:20px; + } + &::-ms-thumb { + width: $range-slider-width; + height: $range-slider-height; + border-radius: $range-slider-border-radius; + background-color: $toggle-handle-off-bg-color; + border-color:$toggle-handle-off-bg-color; + box-shadow: $range-slider-box-shadow; + margin-left:1px; + margin-right:1px; + outline:none; + } + &::-ms-fill-upper { + height: $range-track-height; + background:$range-default-track-bg; + } + */ } + .range input::-moz-focus-outer { + /* hide the focus outline in Firefox */ + border: 0; } + .range input::-webkit-slider-thumb { + position: relative; + width: 28px; + height: 28px; + border-radius: 50%; + background-color: #fff; + box-shadow: 0 0 2px rgba(0, 0, 0, 0.3), 0 3px 5px rgba(0, 0, 0, 0.2); + cursor: pointer; + -webkit-appearance: none; + border: 0; } + .range input::-webkit-slider-thumb:before { + /* what creates the colorful line on the left side of the slider */ + position: absolute; + top: 13px; + left: -2001px; + width: 2000px; + height: 2px; + background: #444; + content: ' '; } + .range input::-webkit-slider-thumb:after { + /* create a larger (but hidden) hit area */ + position: absolute; + top: -15px; + left: -15px; + padding: 30px; + content: ' '; } + .range input::-ms-fill-lower { + height: 2px; + background: #444; } + +.range { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + -moz-align-items: center; + align-items: center; + padding: 2px 11px; } + .range.range-light input::-webkit-slider-thumb:before { + background: #ddd; } + .range.range-light input::-ms-fill-lower { + background: #ddd; } + .range.range-stable input::-webkit-slider-thumb:before { + background: #b2b2b2; } + .range.range-stable input::-ms-fill-lower { + background: #b2b2b2; } + .range.range-positive input::-webkit-slider-thumb:before { + background: #387ef5; } + .range.range-positive input::-ms-fill-lower { + background: #387ef5; } + .range.range-calm input::-webkit-slider-thumb:before { + background: #11c1f3; } + .range.range-calm input::-ms-fill-lower { + background: #11c1f3; } + .range.range-balanced input::-webkit-slider-thumb:before { + background: #33cd5f; } + .range.range-balanced input::-ms-fill-lower { + background: #33cd5f; } + .range.range-assertive input::-webkit-slider-thumb:before { + background: #ef473a; } + .range.range-assertive input::-ms-fill-lower { + background: #ef473a; } + .range.range-energized input::-webkit-slider-thumb:before { + background: #ffc900; } + .range.range-energized input::-ms-fill-lower { + background: #ffc900; } + .range.range-royal input::-webkit-slider-thumb:before { + background: #886aea; } + .range.range-royal input::-ms-fill-lower { + background: #886aea; } + .range.range-dark input::-webkit-slider-thumb:before { + background: #444; } + .range.range-dark input::-ms-fill-lower { + background: #444; } + +.range .icon { + -webkit-box-flex: 0; + -webkit-flex: 0; + -moz-box-flex: 0; + -moz-flex: 0; + -ms-flex: 0; + flex: 0; + display: block; + min-width: 24px; + text-align: center; + font-size: 24px; } + +.range input { + -webkit-box-flex: 1; + -webkit-flex: 1; + -moz-box-flex: 1; + -moz-flex: 1; + -ms-flex: 1; + flex: 1; + display: block; + margin-right: 10px; + margin-left: 10px; } + +.range-label { + -webkit-box-flex: 0; + -webkit-flex: 0 0 auto; + -moz-box-flex: 0; + -moz-flex: 0 0 auto; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + display: block; + white-space: nowrap; } + +.range-label:first-child { + padding-left: 5px; } + +.range input + .range-label { + padding-right: 5px; + padding-left: 0; } + +.platform-windowsphone .range input { + height: auto; } + +/** + * Select + * -------------------------------------------------- + */ +.item-select { + position: relative; } + .item-select select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + position: absolute; + top: 0; + bottom: 0; + right: 0; + padding: 0 48px 0 16px; + max-width: 65%; + border: none; + background: #fff; + color: #333; + text-indent: .01px; + text-overflow: ''; + white-space: nowrap; + font-size: 14px; + cursor: pointer; + direction: rtl; } + .item-select select::-ms-expand { + display: none; } + .item-select option { + direction: ltr; } + .item-select:after { + position: absolute; + top: 50%; + right: 16px; + margin-top: -3px; + width: 0; + height: 0; + border-top: 5px solid; + border-right: 5px solid transparent; + border-left: 5px solid transparent; + color: #999; + content: ""; + pointer-events: none; } + .item-select.item-light select { + background: #fff; + color: #444; } + .item-select.item-stable select { + background: #f8f8f8; + color: #444; } + .item-select.item-stable:after, .item-select.item-stable .input-label { + color: #666666; } + .item-select.item-positive select { + background: #387ef5; + color: #fff; } + .item-select.item-positive:after, .item-select.item-positive .input-label { + color: #fff; } + .item-select.item-calm select { + background: #11c1f3; + color: #fff; } + .item-select.item-calm:after, .item-select.item-calm .input-label { + color: #fff; } + .item-select.item-assertive select { + background: #ef473a; + color: #fff; } + .item-select.item-assertive:after, .item-select.item-assertive .input-label { + color: #fff; } + .item-select.item-balanced select { + background: #33cd5f; + color: #fff; } + .item-select.item-balanced:after, .item-select.item-balanced .input-label { + color: #fff; } + .item-select.item-energized select { + background: #ffc900; + color: #fff; } + .item-select.item-energized:after, .item-select.item-energized .input-label { + color: #fff; } + .item-select.item-royal select { + background: #886aea; + color: #fff; } + .item-select.item-royal:after, .item-select.item-royal .input-label { + color: #fff; } + .item-select.item-dark select { + background: #444; + color: #fff; } + .item-select.item-dark:after, .item-select.item-dark .input-label { + color: #fff; } + +select[multiple], select[size] { + height: auto; } + +/** + * Progress + * -------------------------------------------------- + */ +progress { + display: block; + margin: 15px auto; + width: 100%; } + +/** + * Buttons + * -------------------------------------------------- + */ +.button { + border-color: transparent; + background-color: #f8f8f8; + color: #444; + position: relative; + display: inline-block; + margin: 0; + padding: 0 12px; + min-width: 52px; + min-height: 47px; + border-width: 1px; + border-style: solid; + border-radius: 4px; + vertical-align: top; + text-align: center; + text-overflow: ellipsis; + font-size: 16px; + line-height: 42px; + cursor: pointer; } + .button:hover { + color: #444; + text-decoration: none; } + .button.active, .button.activated { + border-color: #a2a2a2; + background-color: #e5e5e5; } + .button:after { + position: absolute; + top: -6px; + right: -6px; + bottom: -6px; + left: -6px; + content: ' '; } + .button .icon { + vertical-align: top; + pointer-events: none; } + .button .icon:before, .button.icon:before, .button.icon-left:before, .button.icon-right:before { + display: inline-block; + padding: 0 0 1px 0; + vertical-align: inherit; + font-size: 24px; + line-height: 41px; + pointer-events: none; } + .button.icon-left:before { + float: left; + padding-right: .2em; + padding-left: 0; } + .button.icon-right:before { + float: right; + padding-right: 0; + padding-left: .2em; } + .button.button-block, .button.button-full { + margin-top: 10px; + margin-bottom: 10px; } + .button.button-light { + border-color: transparent; + background-color: #fff; + color: #444; } + .button.button-light:hover { + color: #444; + text-decoration: none; } + .button.button-light.active, .button.button-light.activated { + border-color: #a2a2a2; + background-color: #fafafa; } + .button.button-light.button-clear { + border-color: transparent; + background: none; + box-shadow: none; + color: #ddd; } + .button.button-light.button-icon { + border-color: transparent; + background: none; } + .button.button-light.button-outline { + border-color: #ddd; + background: transparent; + color: #ddd; } + .button.button-light.button-outline.active, .button.button-light.button-outline.activated { + background-color: #ddd; + box-shadow: none; + color: #fff; } + .button.button-stable { + border-color: transparent; + background-color: #f8f8f8; + color: #444; } + .button.button-stable:hover { + color: #444; + text-decoration: none; } + .button.button-stable.active, .button.button-stable.activated { + border-color: #a2a2a2; + background-color: #e5e5e5; } + .button.button-stable.button-clear { + border-color: transparent; + background: none; + box-shadow: none; + color: #b2b2b2; } + .button.button-stable.button-icon { + border-color: transparent; + background: none; } + .button.button-stable.button-outline { + border-color: #b2b2b2; + background: transparent; + color: #b2b2b2; } + .button.button-stable.button-outline.active, .button.button-stable.button-outline.activated { + background-color: #b2b2b2; + box-shadow: none; + color: #fff; } + .button.button-positive { + border-color: transparent; + background-color: #387ef5; + color: #fff; } + .button.button-positive:hover { + color: #fff; + text-decoration: none; } + .button.button-positive.active, .button.button-positive.activated { + border-color: #a2a2a2; + background-color: #0c60ee; } + .button.button-positive.button-clear { + border-color: transparent; + background: none; + box-shadow: none; + color: #387ef5; } + .button.button-positive.button-icon { + border-color: transparent; + background: none; } + .button.button-positive.button-outline { + border-color: #387ef5; + background: transparent; + color: #387ef5; } + .button.button-positive.button-outline.active, .button.button-positive.button-outline.activated { + background-color: #387ef5; + box-shadow: none; + color: #fff; } + .button.button-calm { + border-color: transparent; + background-color: #11c1f3; + color: #fff; } + .button.button-calm:hover { + color: #fff; + text-decoration: none; } + .button.button-calm.active, .button.button-calm.activated { + border-color: #a2a2a2; + background-color: #0a9dc7; } + .button.button-calm.button-clear { + border-color: transparent; + background: none; + box-shadow: none; + color: #11c1f3; } + .button.button-calm.button-icon { + border-color: transparent; + background: none; } + .button.button-calm.button-outline { + border-color: #11c1f3; + background: transparent; + color: #11c1f3; } + .button.button-calm.button-outline.active, .button.button-calm.button-outline.activated { + background-color: #11c1f3; + box-shadow: none; + color: #fff; } + .button.button-assertive { + border-color: transparent; + background-color: #ef473a; + color: #fff; } + .button.button-assertive:hover { + color: #fff; + text-decoration: none; } + .button.button-assertive.active, .button.button-assertive.activated { + border-color: #a2a2a2; + background-color: #e42112; } + .button.button-assertive.button-clear { + border-color: transparent; + background: none; + box-shadow: none; + color: #ef473a; } + .button.button-assertive.button-icon { + border-color: transparent; + background: none; } + .button.button-assertive.button-outline { + border-color: #ef473a; + background: transparent; + color: #ef473a; } + .button.button-assertive.button-outline.active, .button.button-assertive.button-outline.activated { + background-color: #ef473a; + box-shadow: none; + color: #fff; } + .button.button-balanced { + border-color: transparent; + background-color: #33cd5f; + color: #fff; } + .button.button-balanced:hover { + color: #fff; + text-decoration: none; } + .button.button-balanced.active, .button.button-balanced.activated { + border-color: #a2a2a2; + background-color: #28a54c; } + .button.button-balanced.button-clear { + border-color: transparent; + background: none; + box-shadow: none; + color: #33cd5f; } + .button.button-balanced.button-icon { + border-color: transparent; + background: none; } + .button.button-balanced.button-outline { + border-color: #33cd5f; + background: transparent; + color: #33cd5f; } + .button.button-balanced.button-outline.active, .button.button-balanced.button-outline.activated { + background-color: #33cd5f; + box-shadow: none; + color: #fff; } + .button.button-energized { + border-color: transparent; + background-color: #ffc900; + color: #fff; } + .button.button-energized:hover { + color: #fff; + text-decoration: none; } + .button.button-energized.active, .button.button-energized.activated { + border-color: #a2a2a2; + background-color: #e6b500; } + .button.button-energized.button-clear { + border-color: transparent; + background: none; + box-shadow: none; + color: #ffc900; } + .button.button-energized.button-icon { + border-color: transparent; + background: none; } + .button.button-energized.button-outline { + border-color: #ffc900; + background: transparent; + color: #ffc900; } + .button.button-energized.button-outline.active, .button.button-energized.button-outline.activated { + background-color: #ffc900; + box-shadow: none; + color: #fff; } + .button.button-royal { + border-color: transparent; + background-color: #886aea; + color: #fff; } + .button.button-royal:hover { + color: #fff; + text-decoration: none; } + .button.button-royal.active, .button.button-royal.activated { + border-color: #a2a2a2; + background-color: #6b46e5; } + .button.button-royal.button-clear { + border-color: transparent; + background: none; + box-shadow: none; + color: #886aea; } + .button.button-royal.button-icon { + border-color: transparent; + background: none; } + .button.button-royal.button-outline { + border-color: #886aea; + background: transparent; + color: #886aea; } + .button.button-royal.button-outline.active, .button.button-royal.button-outline.activated { + background-color: #886aea; + box-shadow: none; + color: #fff; } + .button.button-dark { + border-color: transparent; + background-color: #444; + color: #fff; } + .button.button-dark:hover { + color: #fff; + text-decoration: none; } + .button.button-dark.active, .button.button-dark.activated { + border-color: #a2a2a2; + background-color: #262626; } + .button.button-dark.button-clear { + border-color: transparent; + background: none; + box-shadow: none; + color: #444; } + .button.button-dark.button-icon { + border-color: transparent; + background: none; } + .button.button-dark.button-outline { + border-color: #444; + background: transparent; + color: #444; } + .button.button-dark.button-outline.active, .button.button-dark.button-outline.activated { + background-color: #444; + box-shadow: none; + color: #fff; } + +.button-small { + padding: 2px 4px 1px; + min-width: 28px; + min-height: 30px; + font-size: 12px; + line-height: 26px; } + .button-small .icon:before, .button-small.icon:before, .button-small.icon-left:before, .button-small.icon-right:before { + font-size: 16px; + line-height: 19px; + margin-top: 3px; } + +.button-large { + padding: 0 16px; + min-width: 68px; + min-height: 59px; + font-size: 20px; + line-height: 53px; } + .button-large .icon:before, .button-large.icon:before, .button-large.icon-left:before, .button-large.icon-right:before { + padding-bottom: 2px; + font-size: 32px; + line-height: 51px; } + +.button-icon { + -webkit-transition: opacity 0.1s; + transition: opacity 0.1s; + padding: 0 6px; + min-width: initial; + border-color: transparent; + background: none; } + .button-icon.button.active, .button-icon.button.activated { + border-color: transparent; + background: none; + box-shadow: none; + opacity: 0.3; } + .button-icon .icon:before, .button-icon.icon:before { + font-size: 32px; } + +.button-clear { + -webkit-transition: opacity 0.1s; + transition: opacity 0.1s; + padding: 0 6px; + max-height: 42px; + border-color: transparent; + background: none; + box-shadow: none; } + .button-clear.button-clear { + border-color: transparent; + background: none; + box-shadow: none; + color: transparent; } + .button-clear.button-icon { + border-color: transparent; + background: none; } + .button-clear.active, .button-clear.activated { + opacity: 0.3; } + +.button-outline { + -webkit-transition: opacity 0.1s; + transition: opacity 0.1s; + background: none; + box-shadow: none; } + .button-outline.button-outline { + border-color: transparent; + background: transparent; + color: transparent; } + .button-outline.button-outline.active, .button-outline.button-outline.activated { + background-color: transparent; + box-shadow: none; + color: #fff; } + +.padding > .button.button-block:first-child { + margin-top: 0; } + +.button-block { + display: block; + clear: both; } + .button-block:after { + clear: both; } + +.button-full, +.button-full > .button { + display: block; + margin-right: 0; + margin-left: 0; + border-right-width: 0; + border-left-width: 0; + border-radius: 0; } + +button.button-block, +button.button-full, +.button-full > button.button, +input.button.button-block { + width: 100%; } + +a.button { + text-decoration: none; } + a.button .icon:before, a.button.icon:before, a.button.icon-left:before, a.button.icon-right:before { + margin-top: 2px; } + +.button.disabled, +.button[disabled] { + opacity: .4; + cursor: default !important; + pointer-events: none; } + +/** + * Button Bar + * -------------------------------------------------- + */ +.button-bar { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-flex: 1; + -webkit-flex: 1; + -moz-box-flex: 1; + -moz-flex: 1; + -ms-flex: 1; + flex: 1; + width: 100%; } + .button-bar.button-bar-inline { + display: block; + width: auto; + *zoom: 1; } + .button-bar.button-bar-inline:before, .button-bar.button-bar-inline:after { + display: table; + content: ""; + line-height: 0; } + .button-bar.button-bar-inline:after { + clear: both; } + .button-bar.button-bar-inline > .button { + width: auto; + display: inline-block; + float: left; } + .button-bar.bar-light > .button { + border-color: #ddd; } + .button-bar.bar-stable > .button { + border-color: #b2b2b2; } + .button-bar.bar-positive > .button { + border-color: #0c60ee; } + .button-bar.bar-calm > .button { + border-color: #0a9dc7; } + .button-bar.bar-assertive > .button { + border-color: #e42112; } + .button-bar.bar-balanced > .button { + border-color: #28a54c; } + .button-bar.bar-energized > .button { + border-color: #e6b500; } + .button-bar.bar-royal > .button { + border-color: #6b46e5; } + .button-bar.bar-dark > .button { + border-color: #111; } + +.button-bar > .button { + -webkit-box-flex: 1; + -webkit-flex: 1; + -moz-box-flex: 1; + -moz-flex: 1; + -ms-flex: 1; + flex: 1; + display: block; + overflow: hidden; + padding: 0 16px; + width: 0; + border-width: 1px 0px 1px 1px; + border-radius: 0; + text-align: center; + text-overflow: ellipsis; + white-space: nowrap; } + .button-bar > .button:before, + .button-bar > .button .icon:before { + line-height: 44px; } + .button-bar > .button:first-child { + border-radius: 4px 0px 0px 4px; } + .button-bar > .button:last-child { + border-right-width: 1px; + border-radius: 0px 4px 4px 0px; } + .button-bar > .button:only-child { + border-radius: 4px; } + +.button-bar > .button-small:before, +.button-bar > .button-small .icon:before { + line-height: 28px; } + +/** + * Grid + * -------------------------------------------------- + * Using flexbox for the grid, inspired by Philip Walton: + * http://philipwalton.github.io/solved-by-flexbox/demos/grids/ + * By default each .col within a .row will evenly take up + * available width, and the height of each .col with take + * up the height of the tallest .col in the same .row. + */ +.row { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + padding: 5px; + width: 100%; } + +.row-wrap { + -webkit-flex-wrap: wrap; + -moz-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; } + +.row-no-padding { + padding: 0; } + .row-no-padding > .col { + padding: 0; } + +.row + .row { + margin-top: -5px; + padding-top: 0; } + +.col { + -webkit-box-flex: 1; + -webkit-flex: 1; + -moz-box-flex: 1; + -moz-flex: 1; + -ms-flex: 1; + flex: 1; + display: block; + padding: 5px; + width: 100%; } + +/* Vertically Align Columns */ +/* .row-* vertically aligns every .col in the .row */ +.row-top { + -webkit-box-align: start; + -ms-flex-align: start; + -webkit-align-items: flex-start; + -moz-align-items: flex-start; + align-items: flex-start; } + +.row-bottom { + -webkit-box-align: end; + -ms-flex-align: end; + -webkit-align-items: flex-end; + -moz-align-items: flex-end; + align-items: flex-end; } + +.row-center { + -webkit-box-align: center; + -ms-flex-align: center; + -webkit-align-items: center; + -moz-align-items: center; + align-items: center; } + +.row-stretch { + -webkit-box-align: stretch; + -ms-flex-align: stretch; + -webkit-align-items: stretch; + -moz-align-items: stretch; + align-items: stretch; } + +.row-baseline { + -webkit-box-align: baseline; + -ms-flex-align: baseline; + -webkit-align-items: baseline; + -moz-align-items: baseline; + align-items: baseline; } + +/* .col-* vertically aligns an individual .col */ +.col-top { + -webkit-align-self: flex-start; + -moz-align-self: flex-start; + -ms-flex-item-align: start; + align-self: flex-start; } + +.col-bottom { + -webkit-align-self: flex-end; + -moz-align-self: flex-end; + -ms-flex-item-align: end; + align-self: flex-end; } + +.col-center { + -webkit-align-self: center; + -moz-align-self: center; + -ms-flex-item-align: center; + align-self: center; } + +/* Column Offsets */ +.col-offset-10 { + margin-left: 10%; } + +.col-offset-20 { + margin-left: 20%; } + +.col-offset-25 { + margin-left: 25%; } + +.col-offset-33, .col-offset-34 { + margin-left: 33.3333%; } + +.col-offset-50 { + margin-left: 50%; } + +.col-offset-66, .col-offset-67 { + margin-left: 66.6666%; } + +.col-offset-75 { + margin-left: 75%; } + +.col-offset-80 { + margin-left: 80%; } + +.col-offset-90 { + margin-left: 90%; } + +/* Explicit Column Percent Sizes */ +/* By default each grid column will evenly distribute */ +/* across the grid. However, you can specify individual */ +/* columns to take up a certain size of the available area */ +.col-10 { + -webkit-box-flex: 0; + -webkit-flex: 0 0 10%; + -moz-box-flex: 0; + -moz-flex: 0 0 10%; + -ms-flex: 0 0 10%; + flex: 0 0 10%; + max-width: 10%; } + +.col-20 { + -webkit-box-flex: 0; + -webkit-flex: 0 0 20%; + -moz-box-flex: 0; + -moz-flex: 0 0 20%; + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20%; } + +.col-25 { + -webkit-box-flex: 0; + -webkit-flex: 0 0 25%; + -moz-box-flex: 0; + -moz-flex: 0 0 25%; + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; } + +.col-33, .col-34 { + -webkit-box-flex: 0; + -webkit-flex: 0 0 33.3333%; + -moz-box-flex: 0; + -moz-flex: 0 0 33.3333%; + -ms-flex: 0 0 33.3333%; + flex: 0 0 33.3333%; + max-width: 33.3333%; } + +.col-40 { + -webkit-box-flex: 0; + -webkit-flex: 0 0 40%; + -moz-box-flex: 0; + -moz-flex: 0 0 40%; + -ms-flex: 0 0 40%; + flex: 0 0 40%; + max-width: 40%; } + +.col-50 { + -webkit-box-flex: 0; + -webkit-flex: 0 0 50%; + -moz-box-flex: 0; + -moz-flex: 0 0 50%; + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; } + +.col-60 { + -webkit-box-flex: 0; + -webkit-flex: 0 0 60%; + -moz-box-flex: 0; + -moz-flex: 0 0 60%; + -ms-flex: 0 0 60%; + flex: 0 0 60%; + max-width: 60%; } + +.col-66, .col-67 { + -webkit-box-flex: 0; + -webkit-flex: 0 0 66.6666%; + -moz-box-flex: 0; + -moz-flex: 0 0 66.6666%; + -ms-flex: 0 0 66.6666%; + flex: 0 0 66.6666%; + max-width: 66.6666%; } + +.col-75 { + -webkit-box-flex: 0; + -webkit-flex: 0 0 75%; + -moz-box-flex: 0; + -moz-flex: 0 0 75%; + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75%; } + +.col-80 { + -webkit-box-flex: 0; + -webkit-flex: 0 0 80%; + -moz-box-flex: 0; + -moz-flex: 0 0 80%; + -ms-flex: 0 0 80%; + flex: 0 0 80%; + max-width: 80%; } + +.col-90 { + -webkit-box-flex: 0; + -webkit-flex: 0 0 90%; + -moz-box-flex: 0; + -moz-flex: 0 0 90%; + -ms-flex: 0 0 90%; + flex: 0 0 90%; + max-width: 90%; } + +/* Responsive Grid Classes */ +/* Adding a class of responsive-X to a row */ +/* will trigger the flex-direction to */ +/* change to column and add some margin */ +/* to any columns in the row for clearity */ +@media (max-width: 567px) { + .responsive-sm { + -webkit-box-direction: normal; + -moz-box-direction: normal; + -webkit-box-orient: vertical; + -moz-box-orient: vertical; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; } + .responsive-sm .col, .responsive-sm .col-10, .responsive-sm .col-20, .responsive-sm .col-25, .responsive-sm .col-33, .responsive-sm .col-34, .responsive-sm .col-50, .responsive-sm .col-66, .responsive-sm .col-67, .responsive-sm .col-75, .responsive-sm .col-80, .responsive-sm .col-90 { + -webkit-box-flex: 1; + -webkit-flex: 1; + -moz-box-flex: 1; + -moz-flex: 1; + -ms-flex: 1; + flex: 1; + margin-bottom: 15px; + margin-left: 0; + max-width: 100%; + width: 100%; } } + +@media (max-width: 767px) { + .responsive-md { + -webkit-box-direction: normal; + -moz-box-direction: normal; + -webkit-box-orient: vertical; + -moz-box-orient: vertical; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; } + .responsive-md .col, .responsive-md .col-10, .responsive-md .col-20, .responsive-md .col-25, .responsive-md .col-33, .responsive-md .col-34, .responsive-md .col-50, .responsive-md .col-66, .responsive-md .col-67, .responsive-md .col-75, .responsive-md .col-80, .responsive-md .col-90 { + -webkit-box-flex: 1; + -webkit-flex: 1; + -moz-box-flex: 1; + -moz-flex: 1; + -ms-flex: 1; + flex: 1; + margin-bottom: 15px; + margin-left: 0; + max-width: 100%; + width: 100%; } } + +@media (max-width: 1023px) { + .responsive-lg { + -webkit-box-direction: normal; + -moz-box-direction: normal; + -webkit-box-orient: vertical; + -moz-box-orient: vertical; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; } + .responsive-lg .col, .responsive-lg .col-10, .responsive-lg .col-20, .responsive-lg .col-25, .responsive-lg .col-33, .responsive-lg .col-34, .responsive-lg .col-50, .responsive-lg .col-66, .responsive-lg .col-67, .responsive-lg .col-75, .responsive-lg .col-80, .responsive-lg .col-90 { + -webkit-box-flex: 1; + -webkit-flex: 1; + -moz-box-flex: 1; + -moz-flex: 1; + -ms-flex: 1; + flex: 1; + margin-bottom: 15px; + margin-left: 0; + max-width: 100%; + width: 100%; } } + +/** + * Utility Classes + * -------------------------------------------------- + */ +.hide { + display: none; } + +.opacity-hide { + opacity: 0; } + +.grade-b .opacity-hide, +.grade-c .opacity-hide { + opacity: 1; + display: none; } + +.show { + display: block; } + +.opacity-show { + opacity: 1; } + +.invisible { + visibility: hidden; } + +.keyboard-open .hide-on-keyboard-open { + display: none; } + +.keyboard-open .tabs.hide-on-keyboard-open + .pane .has-tabs, +.keyboard-open .bar-footer.hide-on-keyboard-open + .pane .has-footer { + bottom: 0; } + +.inline { + display: inline-block; } + +.disable-pointer-events { + pointer-events: none; } + +.enable-pointer-events { + pointer-events: auto; } + +.disable-user-behavior { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-touch-callout: none; + -webkit-tap-highlight-color: transparent; + -webkit-tap-highlight-color: transparent; + -webkit-user-drag: none; + -ms-touch-action: none; + -ms-content-zooming: none; } + +.click-block { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + opacity: 0; + z-index: 99999; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + overflow: hidden; } + +.click-block-hide { + -webkit-transform: translate3d(-9999px, 0, 0); + transform: translate3d(-9999px, 0, 0); } + +.no-resize { + resize: none; } + +.block { + display: block; + clear: both; } + .block:after { + display: block; + visibility: hidden; + clear: both; + height: 0; + content: "."; } + +.full-image { + width: 100%; } + +.clearfix { + *zoom: 1; } + .clearfix:before, .clearfix:after { + display: table; + content: ""; + line-height: 0; } + .clearfix:after { + clear: both; } + +/** + * Content Padding + * -------------------------------------------------- + */ +.padding { + padding: 10px; } + +.padding-top, +.padding-vertical { + padding-top: 10px; } + +.padding-right, +.padding-horizontal { + padding-right: 10px; } + +.padding-bottom, +.padding-vertical { + padding-bottom: 10px; } + +.padding-left, +.padding-horizontal { + padding-left: 10px; } + +/** + * Scrollable iFrames + * -------------------------------------------------- + */ +.iframe-wrapper { + position: fixed; + -webkit-overflow-scrolling: touch; + overflow: scroll; } + .iframe-wrapper iframe { + height: 100%; + width: 100%; } + +/** + * Rounded + * -------------------------------------------------- + */ +.rounded { + border-radius: 4px; } + +/** + * Utility Colors + * -------------------------------------------------- + * Utility colors are added to help set a naming convention. You'll + * notice we purposely do not use words like "red" or "blue", but + * instead have colors which represent an emotion or generic theme. + */ +.light, a.light { + color: #fff; } + +.light-bg { + background-color: #fff; } + +.light-border { + border-color: #ddd; } + +.stable, a.stable { + color: #f8f8f8; } + +.stable-bg { + background-color: #f8f8f8; } + +.stable-border { + border-color: #b2b2b2; } + +.positive, a.positive { + color: #387ef5; } + +.positive-bg { + background-color: #387ef5; } + +.positive-border { + border-color: #0c60ee; } + +.calm, a.calm { + color: #11c1f3; } + +.calm-bg { + background-color: #11c1f3; } + +.calm-border { + border-color: #0a9dc7; } + +.assertive, a.assertive { + color: #ef473a; } + +.assertive-bg { + background-color: #ef473a; } + +.assertive-border { + border-color: #e42112; } + +.balanced, a.balanced { + color: #33cd5f; } + +.balanced-bg { + background-color: #33cd5f; } + +.balanced-border { + border-color: #28a54c; } + +.energized, a.energized { + color: #ffc900; } + +.energized-bg { + background-color: #ffc900; } + +.energized-border { + border-color: #e6b500; } + +.royal, a.royal { + color: #886aea; } + +.royal-bg { + background-color: #886aea; } + +.royal-border { + border-color: #6b46e5; } + +.dark, a.dark { + color: #444; } + +.dark-bg { + background-color: #444; } + +.dark-border { + border-color: #111; } + +[collection-repeat] { + /* Position is set by transforms */ + left: 0 !important; + top: 0 !important; + position: absolute !important; + z-index: 1; } + +.collection-repeat-container { + position: relative; + z-index: 1; } + +.collection-repeat-after-container { + z-index: 0; + display: block; + /* when scrolling horizontally, make sure the after container doesn't take up 100% width */ } + .collection-repeat-after-container.horizontal { + display: inline-block; } + +[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, +.x-ng-cloak, .ng-hide:not(.ng-hide-animate) { + display: none !important; } + +/** + * Platform + * -------------------------------------------------- + * Platform specific tweaks + */ +.platform-ios.platform-cordova:not(.fullscreen) .bar-header:not(.bar-subheader) { + height: 64px; } + .platform-ios.platform-cordova:not(.fullscreen) .bar-header:not(.bar-subheader).item-input-inset .item-input-wrapper { + margin-top: 19px !important; } + .platform-ios.platform-cordova:not(.fullscreen) .bar-header:not(.bar-subheader) > * { + margin-top: 20px; } + +.platform-ios.platform-cordova:not(.fullscreen) .tabs-top > .tabs, +.platform-ios.platform-cordova:not(.fullscreen) .tabs.tabs-top { + top: 64px; } + +.platform-ios.platform-cordova:not(.fullscreen) .has-header, +.platform-ios.platform-cordova:not(.fullscreen) .bar-subheader { + top: 64px; } + +.platform-ios.platform-cordova:not(.fullscreen) .has-subheader { + top: 108px; } + +.platform-ios.platform-cordova:not(.fullscreen) .has-header.has-tabs-top { + top: 113px; } + +.platform-ios.platform-cordova:not(.fullscreen) .has-header.has-subheader.has-tabs-top { + top: 157px; } + +.platform-ios.platform-cordova .popover .bar-header:not(.bar-subheader) { + height: 44px; } + .platform-ios.platform-cordova .popover .bar-header:not(.bar-subheader).item-input-inset .item-input-wrapper { + margin-top: -1px; } + .platform-ios.platform-cordova .popover .bar-header:not(.bar-subheader) > * { + margin-top: 0; } + +.platform-ios.platform-cordova .popover .has-header, +.platform-ios.platform-cordova .popover .bar-subheader { + top: 44px; } + +.platform-ios.platform-cordova .popover .has-subheader { + top: 88px; } + +.platform-ios.platform-cordova.status-bar-hide { + margin-bottom: 20px; } + +@media (orientation: landscape) { + .platform-ios.platform-browser.platform-ipad { + position: fixed; } } + +.platform-c:not(.enable-transitions) * { + -webkit-transition: none !important; + transition: none !important; } + +.slide-in-up { + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); } + +.slide-in-up.ng-enter, +.slide-in-up > .ng-enter { + -webkit-transition: all cubic-bezier(0.1, 0.7, 0.1, 1) 400ms; + transition: all cubic-bezier(0.1, 0.7, 0.1, 1) 400ms; } + +.slide-in-up.ng-enter-active, +.slide-in-up > .ng-enter-active { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); } + +.slide-in-up.ng-leave, +.slide-in-up > .ng-leave { + -webkit-transition: all ease-in-out 250ms; + transition: all ease-in-out 250ms; } + +@-webkit-keyframes scaleOut { + from { + -webkit-transform: scale(1); + opacity: 1; } + to { + -webkit-transform: scale(0.8); + opacity: 0; } } + +@keyframes scaleOut { + from { + transform: scale(1); + opacity: 1; } + to { + transform: scale(0.8); + opacity: 0; } } + +@-webkit-keyframes superScaleIn { + from { + -webkit-transform: scale(1.2); + opacity: 0; } + to { + -webkit-transform: scale(1); + opacity: 1; } } + +@keyframes superScaleIn { + from { + transform: scale(1.2); + opacity: 0; } + to { + transform: scale(1); + opacity: 1; } } + +[nav-view-transition="ios"] [nav-view="entering"], +[nav-view-transition="ios"] [nav-view="leaving"] { + -webkit-transition-duration: 500ms; + transition-duration: 500ms; + -webkit-transition-timing-function: cubic-bezier(0.36, 0.66, 0.04, 1); + transition-timing-function: cubic-bezier(0.36, 0.66, 0.04, 1); + -webkit-transition-property: opacity, -webkit-transform, box-shadow; + transition-property: opacity, transform, box-shadow; } + +[nav-view-transition="ios"][nav-view-direction="forward"], [nav-view-transition="ios"][nav-view-direction="back"] { + background-color: #000; } + +[nav-view-transition="ios"] [nav-view="active"], +[nav-view-transition="ios"][nav-view-direction="forward"] [nav-view="entering"], +[nav-view-transition="ios"][nav-view-direction="back"] [nav-view="leaving"] { + z-index: 3; } + +[nav-view-transition="ios"][nav-view-direction="back"] [nav-view="entering"], +[nav-view-transition="ios"][nav-view-direction="forward"] [nav-view="leaving"] { + z-index: 2; } + +[nav-bar-transition="ios"] .title, +[nav-bar-transition="ios"] .buttons, +[nav-bar-transition="ios"] .back-text { + -webkit-transition-duration: 500ms; + transition-duration: 500ms; + -webkit-transition-timing-function: cubic-bezier(0.36, 0.66, 0.04, 1); + transition-timing-function: cubic-bezier(0.36, 0.66, 0.04, 1); + -webkit-transition-property: opacity, -webkit-transform; + transition-property: opacity, transform; } + +[nav-bar-transition="ios"] [nav-bar="active"], +[nav-bar-transition="ios"] [nav-bar="entering"] { + z-index: 10; } + [nav-bar-transition="ios"] [nav-bar="active"] .bar, + [nav-bar-transition="ios"] [nav-bar="entering"] .bar { + background: transparent; } + +[nav-bar-transition="ios"] [nav-bar="cached"] { + display: block; } + [nav-bar-transition="ios"] [nav-bar="cached"] .header-item { + display: none; } + +[nav-view-transition="android"] [nav-view="entering"], +[nav-view-transition="android"] [nav-view="leaving"] { + -webkit-transition-duration: 200ms; + transition-duration: 200ms; + -webkit-transition-timing-function: cubic-bezier(0.4, 0.6, 0.2, 1); + transition-timing-function: cubic-bezier(0.4, 0.6, 0.2, 1); + -webkit-transition-property: -webkit-transform; + transition-property: transform; } + +[nav-view-transition="android"] [nav-view="active"], +[nav-view-transition="android"][nav-view-direction="forward"] [nav-view="entering"], +[nav-view-transition="android"][nav-view-direction="back"] [nav-view="leaving"] { + z-index: 3; } + +[nav-view-transition="android"][nav-view-direction="back"] [nav-view="entering"], +[nav-view-transition="android"][nav-view-direction="forward"] [nav-view="leaving"] { + z-index: 2; } + +[nav-bar-transition="android"] .title, +[nav-bar-transition="android"] .buttons { + -webkit-transition-duration: 200ms; + transition-duration: 200ms; + -webkit-transition-timing-function: cubic-bezier(0.4, 0.6, 0.2, 1); + transition-timing-function: cubic-bezier(0.4, 0.6, 0.2, 1); + -webkit-transition-property: opacity; + transition-property: opacity; } + +[nav-bar-transition="android"] [nav-bar="active"], +[nav-bar-transition="android"] [nav-bar="entering"] { + z-index: 10; } + [nav-bar-transition="android"] [nav-bar="active"] .bar, + [nav-bar-transition="android"] [nav-bar="entering"] .bar { + background: transparent; } + +[nav-bar-transition="android"] [nav-bar="cached"] { + display: block; } + [nav-bar-transition="android"] [nav-bar="cached"] .header-item { + display: none; } + +[nav-swipe="fast"] [nav-view], +[nav-swipe="fast"] .title, +[nav-swipe="fast"] .buttons, +[nav-swipe="fast"] .back-text { + -webkit-transition-duration: 50ms; + transition-duration: 50ms; + -webkit-transition-timing-function: linear; + transition-timing-function: linear; } + +[nav-swipe="slow"] [nav-view], +[nav-swipe="slow"] .title, +[nav-swipe="slow"] .buttons, +[nav-swipe="slow"] .back-text { + -webkit-transition-duration: 160ms; + transition-duration: 160ms; + -webkit-transition-timing-function: linear; + transition-timing-function: linear; } + +[nav-view="cached"], +[nav-bar="cached"] { + display: none; } + +[nav-view="stage"] { + opacity: 0; + -webkit-transition-duration: 0; + transition-duration: 0; } + +[nav-bar="stage"] .title, +[nav-bar="stage"] .buttons, +[nav-bar="stage"] .back-text { + position: absolute; + opacity: 0; + -webkit-transition-duration: 0s; + transition-duration: 0s; } diff --git a/app/www/lib/ionic/css/ionic.min.css b/app/www/lib/ionic/css/ionic.min.css new file mode 100644 index 0000000..5dddf27 --- /dev/null +++ b/app/www/lib/ionic/css/ionic.min.css @@ -0,0 +1,23 @@ +@charset "UTF-8";/*! + * Copyright 2015 Drifty Co. + * http://drifty.com/ + * + * Ionic, v1.3.1 + * A powerful HTML5 mobile app framework. + * http://ionicframework.com/ + * + * By @maxlynch, @benjsperry, @adamdbradley <3 + * + * Licensed under the MIT license. Please see LICENSE for more information. + * + *//*! + Ionicons, v2.0.1 + Created by Ben Sperry for the Ionic Framework, http://ionicons.com/ + https://twitter.com/benjsperry https://twitter.com/ionicframework + MIT License: https://github.com/driftyco/ionicons + + Android-style icons originally built by Google’s + Material Design Icons: https://github.com/google/material-design-icons + used under CC BY http://creativecommons.org/licenses/by/4.0/ + Modified icons to fit ionicon’s grid from original. +*/@font-face{font-family:Ionicons;src:url(../fonts/ionicons.eot?v=2.0.1);src:url(../fonts/ionicons.eot?v=2.0.1#iefix) format("embedded-opentype"),url(../fonts/ionicons.ttf?v=2.0.1) format("truetype"),url(../fonts/ionicons.woff?v=2.0.1) format("woff"),url(../fonts/ionicons.woff) format("woff"),url(../fonts/ionicons.svg?v=2.0.1#Ionicons) format("svg");font-weight:400;font-style:normal}.ion,.ion-alert-circled:before,.ion-alert:before,.ion-android-add-circle:before,.ion-android-add:before,.ion-android-alarm-clock:before,.ion-android-alert:before,.ion-android-apps:before,.ion-android-archive:before,.ion-android-arrow-back:before,.ion-android-arrow-down:before,.ion-android-arrow-dropdown-circle:before,.ion-android-arrow-dropdown:before,.ion-android-arrow-dropleft-circle:before,.ion-android-arrow-dropleft:before,.ion-android-arrow-dropright-circle:before,.ion-android-arrow-dropright:before,.ion-android-arrow-dropup-circle:before,.ion-android-arrow-dropup:before,.ion-android-arrow-forward:before,.ion-android-arrow-up:before,.ion-android-attach:before,.ion-android-bar:before,.ion-android-bicycle:before,.ion-android-boat:before,.ion-android-bookmark:before,.ion-android-bulb:before,.ion-android-bus:before,.ion-android-calendar:before,.ion-android-call:before,.ion-android-camera:before,.ion-android-cancel:before,.ion-android-car:before,.ion-android-cart:before,.ion-android-chat:before,.ion-android-checkbox-blank:before,.ion-android-checkbox-outline-blank:before,.ion-android-checkbox-outline:before,.ion-android-checkbox:before,.ion-android-checkmark-circle:before,.ion-android-clipboard:before,.ion-android-close:before,.ion-android-cloud-circle:before,.ion-android-cloud-done:before,.ion-android-cloud-outline:before,.ion-android-cloud:before,.ion-android-color-palette:before,.ion-android-compass:before,.ion-android-contact:before,.ion-android-contacts:before,.ion-android-contract:before,.ion-android-create:before,.ion-android-delete:before,.ion-android-desktop:before,.ion-android-document:before,.ion-android-done-all:before,.ion-android-done:before,.ion-android-download:before,.ion-android-drafts:before,.ion-android-exit:before,.ion-android-expand:before,.ion-android-favorite-outline:before,.ion-android-favorite:before,.ion-android-film:before,.ion-android-folder-open:before,.ion-android-folder:before,.ion-android-funnel:before,.ion-android-globe:before,.ion-android-hand:before,.ion-android-hangout:before,.ion-android-happy:before,.ion-android-home:before,.ion-android-image:before,.ion-android-laptop:before,.ion-android-list:before,.ion-android-locate:before,.ion-android-lock:before,.ion-android-mail:before,.ion-android-map:before,.ion-android-menu:before,.ion-android-microphone-off:before,.ion-android-microphone:before,.ion-android-more-horizontal:before,.ion-android-more-vertical:before,.ion-android-navigate:before,.ion-android-notifications-none:before,.ion-android-notifications-off:before,.ion-android-notifications:before,.ion-android-open:before,.ion-android-options:before,.ion-android-people:before,.ion-android-person-add:before,.ion-android-person:before,.ion-android-phone-landscape:before,.ion-android-phone-portrait:before,.ion-android-pin:before,.ion-android-plane:before,.ion-android-playstore:before,.ion-android-print:before,.ion-android-radio-button-off:before,.ion-android-radio-button-on:before,.ion-android-refresh:before,.ion-android-remove-circle:before,.ion-android-remove:before,.ion-android-restaurant:before,.ion-android-sad:before,.ion-android-search:before,.ion-android-send:before,.ion-android-settings:before,.ion-android-share-alt:before,.ion-android-share:before,.ion-android-star-half:before,.ion-android-star-outline:before,.ion-android-star:before,.ion-android-stopwatch:before,.ion-android-subway:before,.ion-android-sunny:before,.ion-android-sync:before,.ion-android-textsms:before,.ion-android-time:before,.ion-android-train:before,.ion-android-unlock:before,.ion-android-upload:before,.ion-android-volume-down:before,.ion-android-volume-mute:before,.ion-android-volume-off:before,.ion-android-volume-up:before,.ion-android-walk:before,.ion-android-warning:before,.ion-android-watch:before,.ion-android-wifi:before,.ion-aperture:before,.ion-archive:before,.ion-arrow-down-a:before,.ion-arrow-down-b:before,.ion-arrow-down-c:before,.ion-arrow-expand:before,.ion-arrow-graph-down-left:before,.ion-arrow-graph-down-right:before,.ion-arrow-graph-up-left:before,.ion-arrow-graph-up-right:before,.ion-arrow-left-a:before,.ion-arrow-left-b:before,.ion-arrow-left-c:before,.ion-arrow-move:before,.ion-arrow-resize:before,.ion-arrow-return-left:before,.ion-arrow-return-right:before,.ion-arrow-right-a:before,.ion-arrow-right-b:before,.ion-arrow-right-c:before,.ion-arrow-shrink:before,.ion-arrow-swap:before,.ion-arrow-up-a:before,.ion-arrow-up-b:before,.ion-arrow-up-c:before,.ion-asterisk:before,.ion-at:before,.ion-backspace-outline:before,.ion-backspace:before,.ion-bag:before,.ion-battery-charging:before,.ion-battery-empty:before,.ion-battery-full:before,.ion-battery-half:before,.ion-battery-low:before,.ion-beaker:before,.ion-beer:before,.ion-bluetooth:before,.ion-bonfire:before,.ion-bookmark:before,.ion-bowtie:before,.ion-briefcase:before,.ion-bug:before,.ion-calculator:before,.ion-calendar:before,.ion-camera:before,.ion-card:before,.ion-cash:before,.ion-chatbox-working:before,.ion-chatbox:before,.ion-chatboxes:before,.ion-chatbubble-working:before,.ion-chatbubble:before,.ion-chatbubbles:before,.ion-checkmark-circled:before,.ion-checkmark-round:before,.ion-checkmark:before,.ion-chevron-down:before,.ion-chevron-left:before,.ion-chevron-right:before,.ion-chevron-up:before,.ion-clipboard:before,.ion-clock:before,.ion-close-circled:before,.ion-close-round:before,.ion-close:before,.ion-closed-captioning:before,.ion-cloud:before,.ion-code-download:before,.ion-code-working:before,.ion-code:before,.ion-coffee:before,.ion-compass:before,.ion-compose:before,.ion-connection-bars:before,.ion-contrast:before,.ion-crop:before,.ion-cube:before,.ion-disc:before,.ion-document-text:before,.ion-document:before,.ion-drag:before,.ion-earth:before,.ion-easel:before,.ion-edit:before,.ion-egg:before,.ion-eject:before,.ion-email-unread:before,.ion-email:before,.ion-erlenmeyer-flask-bubbles:before,.ion-erlenmeyer-flask:before,.ion-eye-disabled:before,.ion-eye:before,.ion-female:before,.ion-filing:before,.ion-film-marker:before,.ion-fireball:before,.ion-flag:before,.ion-flame:before,.ion-flash-off:before,.ion-flash:before,.ion-folder:before,.ion-fork-repo:before,.ion-fork:before,.ion-forward:before,.ion-funnel:before,.ion-gear-a:before,.ion-gear-b:before,.ion-grid:before,.ion-hammer:before,.ion-happy-outline:before,.ion-happy:before,.ion-headphone:before,.ion-heart-broken:before,.ion-heart:before,.ion-help-buoy:before,.ion-help-circled:before,.ion-help:before,.ion-home:before,.ion-icecream:before,.ion-image:before,.ion-images:before,.ion-information-circled:before,.ion-information:before,.ion-ionic:before,.ion-ios-alarm-outline:before,.ion-ios-alarm:before,.ion-ios-albums-outline:before,.ion-ios-albums:before,.ion-ios-americanfootball-outline:before,.ion-ios-americanfootball:before,.ion-ios-analytics-outline:before,.ion-ios-analytics:before,.ion-ios-arrow-back:before,.ion-ios-arrow-down:before,.ion-ios-arrow-forward:before,.ion-ios-arrow-left:before,.ion-ios-arrow-right:before,.ion-ios-arrow-thin-down:before,.ion-ios-arrow-thin-left:before,.ion-ios-arrow-thin-right:before,.ion-ios-arrow-thin-up:before,.ion-ios-arrow-up:before,.ion-ios-at-outline:before,.ion-ios-at:before,.ion-ios-barcode-outline:before,.ion-ios-barcode:before,.ion-ios-baseball-outline:before,.ion-ios-baseball:before,.ion-ios-basketball-outline:before,.ion-ios-basketball:before,.ion-ios-bell-outline:before,.ion-ios-bell:before,.ion-ios-body-outline:before,.ion-ios-body:before,.ion-ios-bolt-outline:before,.ion-ios-bolt:before,.ion-ios-book-outline:before,.ion-ios-book:before,.ion-ios-bookmarks-outline:before,.ion-ios-bookmarks:before,.ion-ios-box-outline:before,.ion-ios-box:before,.ion-ios-briefcase-outline:before,.ion-ios-briefcase:before,.ion-ios-browsers-outline:before,.ion-ios-browsers:before,.ion-ios-calculator-outline:before,.ion-ios-calculator:before,.ion-ios-calendar-outline:before,.ion-ios-calendar:before,.ion-ios-camera-outline:before,.ion-ios-camera:before,.ion-ios-cart-outline:before,.ion-ios-cart:before,.ion-ios-chatboxes-outline:before,.ion-ios-chatboxes:before,.ion-ios-chatbubble-outline:before,.ion-ios-chatbubble:before,.ion-ios-checkmark-empty:before,.ion-ios-checkmark-outline:before,.ion-ios-checkmark:before,.ion-ios-circle-filled:before,.ion-ios-circle-outline:before,.ion-ios-clock-outline:before,.ion-ios-clock:before,.ion-ios-close-empty:before,.ion-ios-close-outline:before,.ion-ios-close:before,.ion-ios-cloud-download-outline:before,.ion-ios-cloud-download:before,.ion-ios-cloud-outline:before,.ion-ios-cloud-upload-outline:before,.ion-ios-cloud-upload:before,.ion-ios-cloud:before,.ion-ios-cloudy-night-outline:before,.ion-ios-cloudy-night:before,.ion-ios-cloudy-outline:before,.ion-ios-cloudy:before,.ion-ios-cog-outline:before,.ion-ios-cog:before,.ion-ios-color-filter-outline:before,.ion-ios-color-filter:before,.ion-ios-color-wand-outline:before,.ion-ios-color-wand:before,.ion-ios-compose-outline:before,.ion-ios-compose:before,.ion-ios-contact-outline:before,.ion-ios-contact:before,.ion-ios-copy-outline:before,.ion-ios-copy:before,.ion-ios-crop-strong:before,.ion-ios-crop:before,.ion-ios-download-outline:before,.ion-ios-download:before,.ion-ios-drag:before,.ion-ios-email-outline:before,.ion-ios-email:before,.ion-ios-eye-outline:before,.ion-ios-eye:before,.ion-ios-fastforward-outline:before,.ion-ios-fastforward:before,.ion-ios-filing-outline:before,.ion-ios-filing:before,.ion-ios-film-outline:before,.ion-ios-film:before,.ion-ios-flag-outline:before,.ion-ios-flag:before,.ion-ios-flame-outline:before,.ion-ios-flame:before,.ion-ios-flask-outline:before,.ion-ios-flask:before,.ion-ios-flower-outline:before,.ion-ios-flower:before,.ion-ios-folder-outline:before,.ion-ios-folder:before,.ion-ios-football-outline:before,.ion-ios-football:before,.ion-ios-game-controller-a-outline:before,.ion-ios-game-controller-a:before,.ion-ios-game-controller-b-outline:before,.ion-ios-game-controller-b:before,.ion-ios-gear-outline:before,.ion-ios-gear:before,.ion-ios-glasses-outline:before,.ion-ios-glasses:before,.ion-ios-grid-view-outline:before,.ion-ios-grid-view:before,.ion-ios-heart-outline:before,.ion-ios-heart:before,.ion-ios-help-empty:before,.ion-ios-help-outline:before,.ion-ios-help:before,.ion-ios-home-outline:before,.ion-ios-home:before,.ion-ios-infinite-outline:before,.ion-ios-infinite:before,.ion-ios-information-empty:before,.ion-ios-information-outline:before,.ion-ios-information:before,.ion-ios-ionic-outline:before,.ion-ios-keypad-outline:before,.ion-ios-keypad:before,.ion-ios-lightbulb-outline:before,.ion-ios-lightbulb:before,.ion-ios-list-outline:before,.ion-ios-list:before,.ion-ios-location-outline:before,.ion-ios-location:before,.ion-ios-locked-outline:before,.ion-ios-locked:before,.ion-ios-loop-strong:before,.ion-ios-loop:before,.ion-ios-medical-outline:before,.ion-ios-medical:before,.ion-ios-medkit-outline:before,.ion-ios-medkit:before,.ion-ios-mic-off:before,.ion-ios-mic-outline:before,.ion-ios-mic:before,.ion-ios-minus-empty:before,.ion-ios-minus-outline:before,.ion-ios-minus:before,.ion-ios-monitor-outline:before,.ion-ios-monitor:before,.ion-ios-moon-outline:before,.ion-ios-moon:before,.ion-ios-more-outline:before,.ion-ios-more:before,.ion-ios-musical-note:before,.ion-ios-musical-notes:before,.ion-ios-navigate-outline:before,.ion-ios-navigate:before,.ion-ios-nutrition-outline:before,.ion-ios-nutrition:before,.ion-ios-paper-outline:before,.ion-ios-paper:before,.ion-ios-paperplane-outline:before,.ion-ios-paperplane:before,.ion-ios-partlysunny-outline:before,.ion-ios-partlysunny:before,.ion-ios-pause-outline:before,.ion-ios-pause:before,.ion-ios-paw-outline:before,.ion-ios-paw:before,.ion-ios-people-outline:before,.ion-ios-people:before,.ion-ios-person-outline:before,.ion-ios-person:before,.ion-ios-personadd-outline:before,.ion-ios-personadd:before,.ion-ios-photos-outline:before,.ion-ios-photos:before,.ion-ios-pie-outline:before,.ion-ios-pie:before,.ion-ios-pint-outline:before,.ion-ios-pint:before,.ion-ios-play-outline:before,.ion-ios-play:before,.ion-ios-plus-empty:before,.ion-ios-plus-outline:before,.ion-ios-plus:before,.ion-ios-pricetag-outline:before,.ion-ios-pricetag:before,.ion-ios-pricetags-outline:before,.ion-ios-pricetags:before,.ion-ios-printer-outline:before,.ion-ios-printer:before,.ion-ios-pulse-strong:before,.ion-ios-pulse:before,.ion-ios-rainy-outline:before,.ion-ios-rainy:before,.ion-ios-recording-outline:before,.ion-ios-recording:before,.ion-ios-redo-outline:before,.ion-ios-redo:before,.ion-ios-refresh-empty:before,.ion-ios-refresh-outline:before,.ion-ios-refresh:before,.ion-ios-reload:before,.ion-ios-reverse-camera-outline:before,.ion-ios-reverse-camera:before,.ion-ios-rewind-outline:before,.ion-ios-rewind:before,.ion-ios-rose-outline:before,.ion-ios-rose:before,.ion-ios-search-strong:before,.ion-ios-search:before,.ion-ios-settings-strong:before,.ion-ios-settings:before,.ion-ios-shuffle-strong:before,.ion-ios-shuffle:before,.ion-ios-skipbackward-outline:before,.ion-ios-skipbackward:before,.ion-ios-skipforward-outline:before,.ion-ios-skipforward:before,.ion-ios-snowy:before,.ion-ios-speedometer-outline:before,.ion-ios-speedometer:before,.ion-ios-star-half:before,.ion-ios-star-outline:before,.ion-ios-star:before,.ion-ios-stopwatch-outline:before,.ion-ios-stopwatch:before,.ion-ios-sunny-outline:before,.ion-ios-sunny:before,.ion-ios-telephone-outline:before,.ion-ios-telephone:before,.ion-ios-tennisball-outline:before,.ion-ios-tennisball:before,.ion-ios-thunderstorm-outline:before,.ion-ios-thunderstorm:before,.ion-ios-time-outline:before,.ion-ios-time:before,.ion-ios-timer-outline:before,.ion-ios-timer:before,.ion-ios-toggle-outline:before,.ion-ios-toggle:before,.ion-ios-trash-outline:before,.ion-ios-trash:before,.ion-ios-undo-outline:before,.ion-ios-undo:before,.ion-ios-unlocked-outline:before,.ion-ios-unlocked:before,.ion-ios-upload-outline:before,.ion-ios-upload:before,.ion-ios-videocam-outline:before,.ion-ios-videocam:before,.ion-ios-volume-high:before,.ion-ios-volume-low:before,.ion-ios-wineglass-outline:before,.ion-ios-wineglass:before,.ion-ios-world-outline:before,.ion-ios-world:before,.ion-ipad:before,.ion-iphone:before,.ion-ipod:before,.ion-jet:before,.ion-key:before,.ion-knife:before,.ion-laptop:before,.ion-leaf:before,.ion-levels:before,.ion-lightbulb:before,.ion-link:before,.ion-load-a:before,.ion-load-b:before,.ion-load-c:before,.ion-load-d:before,.ion-location:before,.ion-lock-combination:before,.ion-locked:before,.ion-log-in:before,.ion-log-out:before,.ion-loop:before,.ion-magnet:before,.ion-male:before,.ion-man:before,.ion-map:before,.ion-medkit:before,.ion-merge:before,.ion-mic-a:before,.ion-mic-b:before,.ion-mic-c:before,.ion-minus-circled:before,.ion-minus-round:before,.ion-minus:before,.ion-model-s:before,.ion-monitor:before,.ion-more:before,.ion-mouse:before,.ion-music-note:before,.ion-navicon-round:before,.ion-navicon:before,.ion-navigate:before,.ion-network:before,.ion-no-smoking:before,.ion-nuclear:before,.ion-outlet:before,.ion-paintbrush:before,.ion-paintbucket:before,.ion-paper-airplane:before,.ion-paperclip:before,.ion-pause:before,.ion-person-add:before,.ion-person-stalker:before,.ion-person:before,.ion-pie-graph:before,.ion-pin:before,.ion-pinpoint:before,.ion-pizza:before,.ion-plane:before,.ion-planet:before,.ion-play:before,.ion-playstation:before,.ion-plus-circled:before,.ion-plus-round:before,.ion-plus:before,.ion-podium:before,.ion-pound:before,.ion-power:before,.ion-pricetag:before,.ion-pricetags:before,.ion-printer:before,.ion-pull-request:before,.ion-qr-scanner:before,.ion-quote:before,.ion-radio-waves:before,.ion-record:before,.ion-refresh:before,.ion-reply-all:before,.ion-reply:before,.ion-ribbon-a:before,.ion-ribbon-b:before,.ion-sad-outline:before,.ion-sad:before,.ion-scissors:before,.ion-search:before,.ion-settings:before,.ion-share:before,.ion-shuffle:before,.ion-skip-backward:before,.ion-skip-forward:before,.ion-social-android-outline:before,.ion-social-android:before,.ion-social-angular-outline:before,.ion-social-angular:before,.ion-social-apple-outline:before,.ion-social-apple:before,.ion-social-bitcoin-outline:before,.ion-social-bitcoin:before,.ion-social-buffer-outline:before,.ion-social-buffer:before,.ion-social-chrome-outline:before,.ion-social-chrome:before,.ion-social-codepen-outline:before,.ion-social-codepen:before,.ion-social-css3-outline:before,.ion-social-css3:before,.ion-social-designernews-outline:before,.ion-social-designernews:before,.ion-social-dribbble-outline:before,.ion-social-dribbble:before,.ion-social-dropbox-outline:before,.ion-social-dropbox:before,.ion-social-euro-outline:before,.ion-social-euro:before,.ion-social-facebook-outline:before,.ion-social-facebook:before,.ion-social-foursquare-outline:before,.ion-social-foursquare:before,.ion-social-freebsd-devil:before,.ion-social-github-outline:before,.ion-social-github:before,.ion-social-google-outline:before,.ion-social-google:before,.ion-social-googleplus-outline:before,.ion-social-googleplus:before,.ion-social-hackernews-outline:before,.ion-social-hackernews:before,.ion-social-html5-outline:before,.ion-social-html5:before,.ion-social-instagram-outline:before,.ion-social-instagram:before,.ion-social-javascript-outline:before,.ion-social-javascript:before,.ion-social-linkedin-outline:before,.ion-social-linkedin:before,.ion-social-markdown:before,.ion-social-nodejs:before,.ion-social-octocat:before,.ion-social-pinterest-outline:before,.ion-social-pinterest:before,.ion-social-python:before,.ion-social-reddit-outline:before,.ion-social-reddit:before,.ion-social-rss-outline:before,.ion-social-rss:before,.ion-social-sass:before,.ion-social-skype-outline:before,.ion-social-skype:before,.ion-social-snapchat-outline:before,.ion-social-snapchat:before,.ion-social-tumblr-outline:before,.ion-social-tumblr:before,.ion-social-tux:before,.ion-social-twitch-outline:before,.ion-social-twitch:before,.ion-social-twitter-outline:before,.ion-social-twitter:before,.ion-social-usd-outline:before,.ion-social-usd:before,.ion-social-vimeo-outline:before,.ion-social-vimeo:before,.ion-social-whatsapp-outline:before,.ion-social-whatsapp:before,.ion-social-windows-outline:before,.ion-social-windows:before,.ion-social-wordpress-outline:before,.ion-social-wordpress:before,.ion-social-yahoo-outline:before,.ion-social-yahoo:before,.ion-social-yen-outline:before,.ion-social-yen:before,.ion-social-youtube-outline:before,.ion-social-youtube:before,.ion-soup-can-outline:before,.ion-soup-can:before,.ion-speakerphone:before,.ion-speedometer:before,.ion-spoon:before,.ion-star:before,.ion-stats-bars:before,.ion-steam:before,.ion-stop:before,.ion-thermometer:before,.ion-thumbsdown:before,.ion-thumbsup:before,.ion-toggle-filled:before,.ion-toggle:before,.ion-transgender:before,.ion-trash-a:before,.ion-trash-b:before,.ion-trophy:before,.ion-tshirt-outline:before,.ion-tshirt:before,.ion-umbrella:before,.ion-university:before,.ion-unlocked:before,.ion-upload:before,.ion-usb:before,.ion-videocamera:before,.ion-volume-high:before,.ion-volume-low:before,.ion-volume-medium:before,.ion-volume-mute:before,.ion-wand:before,.ion-waterdrop:before,.ion-wifi:before,.ion-wineglass:before,.ion-woman:before,.ion-wrench:before,.ion-xbox:before,.ionicons{display:inline-block;font-family:Ionicons;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;text-rendering:auto;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.ion-alert:before{content:""}.ion-alert-circled:before{content:""}.ion-android-add:before{content:""}.ion-android-add-circle:before{content:""}.ion-android-alarm-clock:before{content:""}.ion-android-alert:before{content:""}.ion-android-apps:before{content:""}.ion-android-archive:before{content:""}.ion-android-arrow-back:before{content:""}.ion-android-arrow-down:before{content:""}.ion-android-arrow-dropdown:before{content:""}.ion-android-arrow-dropdown-circle:before{content:""}.ion-android-arrow-dropleft:before{content:""}.ion-android-arrow-dropleft-circle:before{content:""}.ion-android-arrow-dropright:before{content:""}.ion-android-arrow-dropright-circle:before{content:""}.ion-android-arrow-dropup:before{content:""}.ion-android-arrow-dropup-circle:before{content:""}.ion-android-arrow-forward:before{content:""}.ion-android-arrow-up:before{content:""}.ion-android-attach:before{content:""}.ion-android-bar:before{content:""}.ion-android-bicycle:before{content:""}.ion-android-boat:before{content:""}.ion-android-bookmark:before{content:""}.ion-android-bulb:before{content:""}.ion-android-bus:before{content:""}.ion-android-calendar:before{content:""}.ion-android-call:before{content:""}.ion-android-camera:before{content:""}.ion-android-cancel:before{content:""}.ion-android-car:before{content:""}.ion-android-cart:before{content:""}.ion-android-chat:before{content:""}.ion-android-checkbox:before{content:""}.ion-android-checkbox-blank:before{content:""}.ion-android-checkbox-outline:before{content:""}.ion-android-checkbox-outline-blank:before{content:""}.ion-android-checkmark-circle:before{content:""}.ion-android-clipboard:before{content:""}.ion-android-close:before{content:""}.ion-android-cloud:before{content:""}.ion-android-cloud-circle:before{content:""}.ion-android-cloud-done:before{content:""}.ion-android-cloud-outline:before{content:""}.ion-android-color-palette:before{content:""}.ion-android-compass:before{content:""}.ion-android-contact:before{content:""}.ion-android-contacts:before{content:""}.ion-android-contract:before{content:""}.ion-android-create:before{content:""}.ion-android-delete:before{content:""}.ion-android-desktop:before{content:""}.ion-android-document:before{content:""}.ion-android-done:before{content:""}.ion-android-done-all:before{content:""}.ion-android-download:before{content:""}.ion-android-drafts:before{content:""}.ion-android-exit:before{content:""}.ion-android-expand:before{content:""}.ion-android-favorite:before{content:""}.ion-android-favorite-outline:before{content:""}.ion-android-film:before{content:""}.ion-android-folder:before{content:""}.ion-android-folder-open:before{content:""}.ion-android-funnel:before{content:""}.ion-android-globe:before{content:""}.ion-android-hand:before{content:""}.ion-android-hangout:before{content:""}.ion-android-happy:before{content:""}.ion-android-home:before{content:""}.ion-android-image:before{content:""}.ion-android-laptop:before{content:""}.ion-android-list:before{content:""}.ion-android-locate:before{content:""}.ion-android-lock:before{content:""}.ion-android-mail:before{content:""}.ion-android-map:before{content:""}.ion-android-menu:before{content:""}.ion-android-microphone:before{content:""}.ion-android-microphone-off:before{content:""}.ion-android-more-horizontal:before{content:""}.ion-android-more-vertical:before{content:""}.ion-android-navigate:before{content:""}.ion-android-notifications:before{content:""}.ion-android-notifications-none:before{content:""}.ion-android-notifications-off:before{content:""}.ion-android-open:before{content:""}.ion-android-options:before{content:""}.ion-android-people:before{content:""}.ion-android-person:before{content:""}.ion-android-person-add:before{content:""}.ion-android-phone-landscape:before{content:""}.ion-android-phone-portrait:before{content:""}.ion-android-pin:before{content:""}.ion-android-plane:before{content:""}.ion-android-playstore:before{content:""}.ion-android-print:before{content:""}.ion-android-radio-button-off:before{content:""}.ion-android-radio-button-on:before{content:""}.ion-android-refresh:before{content:""}.ion-android-remove:before{content:""}.ion-android-remove-circle:before{content:""}.ion-android-restaurant:before{content:""}.ion-android-sad:before{content:""}.ion-android-search:before{content:""}.ion-android-send:before{content:""}.ion-android-settings:before{content:""}.ion-android-share:before{content:""}.ion-android-share-alt:before{content:""}.ion-android-star:before{content:""}.ion-android-star-half:before{content:""}.ion-android-star-outline:before{content:""}.ion-android-stopwatch:before{content:""}.ion-android-subway:before{content:""}.ion-android-sunny:before{content:""}.ion-android-sync:before{content:""}.ion-android-textsms:before{content:""}.ion-android-time:before{content:""}.ion-android-train:before{content:""}.ion-android-unlock:before{content:""}.ion-android-upload:before{content:""}.ion-android-volume-down:before{content:""}.ion-android-volume-mute:before{content:""}.ion-android-volume-off:before{content:""}.ion-android-volume-up:before{content:""}.ion-android-walk:before{content:""}.ion-android-warning:before{content:""}.ion-android-watch:before{content:""}.ion-android-wifi:before{content:""}.ion-aperture:before{content:""}.ion-archive:before{content:""}.ion-arrow-down-a:before{content:""}.ion-arrow-down-b:before{content:""}.ion-arrow-down-c:before{content:""}.ion-arrow-expand:before{content:""}.ion-arrow-graph-down-left:before{content:""}.ion-arrow-graph-down-right:before{content:""}.ion-arrow-graph-up-left:before{content:""}.ion-arrow-graph-up-right:before{content:""}.ion-arrow-left-a:before{content:""}.ion-arrow-left-b:before{content:""}.ion-arrow-left-c:before{content:""}.ion-arrow-move:before{content:""}.ion-arrow-resize:before{content:""}.ion-arrow-return-left:before{content:""}.ion-arrow-return-right:before{content:""}.ion-arrow-right-a:before{content:""}.ion-arrow-right-b:before{content:""}.ion-arrow-right-c:before{content:""}.ion-arrow-shrink:before{content:""}.ion-arrow-swap:before{content:""}.ion-arrow-up-a:before{content:""}.ion-arrow-up-b:before{content:""}.ion-arrow-up-c:before{content:""}.ion-asterisk:before{content:""}.ion-at:before{content:""}.ion-backspace:before{content:""}.ion-backspace-outline:before{content:""}.ion-bag:before{content:""}.ion-battery-charging:before{content:""}.ion-battery-empty:before{content:""}.ion-battery-full:before{content:""}.ion-battery-half:before{content:""}.ion-battery-low:before{content:""}.ion-beaker:before{content:""}.ion-beer:before{content:""}.ion-bluetooth:before{content:""}.ion-bonfire:before{content:""}.ion-bookmark:before{content:""}.ion-bowtie:before{content:""}.ion-briefcase:before{content:""}.ion-bug:before{content:""}.ion-calculator:before{content:""}.ion-calendar:before{content:""}.ion-camera:before{content:""}.ion-card:before{content:""}.ion-cash:before{content:""}.ion-chatbox:before{content:""}.ion-chatbox-working:before{content:""}.ion-chatboxes:before{content:""}.ion-chatbubble:before{content:""}.ion-chatbubble-working:before{content:""}.ion-chatbubbles:before{content:""}.ion-checkmark:before{content:""}.ion-checkmark-circled:before{content:""}.ion-checkmark-round:before{content:""}.ion-chevron-down:before{content:""}.ion-chevron-left:before{content:""}.ion-chevron-right:before{content:""}.ion-chevron-up:before{content:""}.ion-clipboard:before{content:""}.ion-clock:before{content:""}.ion-close:before{content:""}.ion-close-circled:before{content:""}.ion-close-round:before{content:""}.ion-closed-captioning:before{content:""}.ion-cloud:before{content:""}.ion-code:before{content:""}.ion-code-download:before{content:""}.ion-code-working:before{content:""}.ion-coffee:before{content:""}.ion-compass:before{content:""}.ion-compose:before{content:""}.ion-connection-bars:before{content:""}.ion-contrast:before{content:""}.ion-crop:before{content:""}.ion-cube:before{content:""}.ion-disc:before{content:""}.ion-document:before{content:""}.ion-document-text:before{content:""}.ion-drag:before{content:""}.ion-earth:before{content:""}.ion-easel:before{content:""}.ion-edit:before{content:""}.ion-egg:before{content:""}.ion-eject:before{content:""}.ion-email:before{content:""}.ion-email-unread:before{content:""}.ion-erlenmeyer-flask:before{content:""}.ion-erlenmeyer-flask-bubbles:before{content:""}.ion-eye:before{content:""}.ion-eye-disabled:before{content:""}.ion-female:before{content:""}.ion-filing:before{content:""}.ion-film-marker:before{content:""}.ion-fireball:before{content:""}.ion-flag:before{content:""}.ion-flame:before{content:""}.ion-flash:before{content:""}.ion-flash-off:before{content:""}.ion-folder:before{content:""}.ion-fork:before{content:""}.ion-fork-repo:before{content:""}.ion-forward:before{content:""}.ion-funnel:before{content:""}.ion-gear-a:before{content:""}.ion-gear-b:before{content:""}.ion-grid:before{content:""}.ion-hammer:before{content:""}.ion-happy:before{content:""}.ion-happy-outline:before{content:""}.ion-headphone:before{content:""}.ion-heart:before{content:""}.ion-heart-broken:before{content:""}.ion-help:before{content:""}.ion-help-buoy:before{content:""}.ion-help-circled:before{content:""}.ion-home:before{content:""}.ion-icecream:before{content:""}.ion-image:before{content:""}.ion-images:before{content:""}.ion-information:before{content:""}.ion-information-circled:before{content:""}.ion-ionic:before{content:""}.ion-ios-alarm:before{content:""}.ion-ios-alarm-outline:before{content:""}.ion-ios-albums:before{content:""}.ion-ios-albums-outline:before{content:""}.ion-ios-americanfootball:before{content:""}.ion-ios-americanfootball-outline:before{content:""}.ion-ios-analytics:before{content:""}.ion-ios-analytics-outline:before{content:""}.ion-ios-arrow-back:before{content:""}.ion-ios-arrow-down:before{content:""}.ion-ios-arrow-forward:before{content:""}.ion-ios-arrow-left:before{content:""}.ion-ios-arrow-right:before{content:""}.ion-ios-arrow-thin-down:before{content:""}.ion-ios-arrow-thin-left:before{content:""}.ion-ios-arrow-thin-right:before{content:""}.ion-ios-arrow-thin-up:before{content:""}.ion-ios-arrow-up:before{content:""}.ion-ios-at:before{content:""}.ion-ios-at-outline:before{content:""}.ion-ios-barcode:before{content:""}.ion-ios-barcode-outline:before{content:""}.ion-ios-baseball:before{content:""}.ion-ios-baseball-outline:before{content:""}.ion-ios-basketball:before{content:""}.ion-ios-basketball-outline:before{content:""}.ion-ios-bell:before{content:""}.ion-ios-bell-outline:before{content:""}.ion-ios-body:before{content:""}.ion-ios-body-outline:before{content:""}.ion-ios-bolt:before{content:""}.ion-ios-bolt-outline:before{content:""}.ion-ios-book:before{content:""}.ion-ios-book-outline:before{content:""}.ion-ios-bookmarks:before{content:""}.ion-ios-bookmarks-outline:before{content:""}.ion-ios-box:before{content:""}.ion-ios-box-outline:before{content:""}.ion-ios-briefcase:before{content:""}.ion-ios-briefcase-outline:before{content:""}.ion-ios-browsers:before{content:""}.ion-ios-browsers-outline:before{content:""}.ion-ios-calculator:before{content:""}.ion-ios-calculator-outline:before{content:""}.ion-ios-calendar:before{content:""}.ion-ios-calendar-outline:before{content:""}.ion-ios-camera:before{content:""}.ion-ios-camera-outline:before{content:""}.ion-ios-cart:before{content:""}.ion-ios-cart-outline:before{content:""}.ion-ios-chatboxes:before{content:""}.ion-ios-chatboxes-outline:before{content:""}.ion-ios-chatbubble:before{content:""}.ion-ios-chatbubble-outline:before{content:""}.ion-ios-checkmark:before{content:""}.ion-ios-checkmark-empty:before{content:""}.ion-ios-checkmark-outline:before{content:""}.ion-ios-circle-filled:before{content:""}.ion-ios-circle-outline:before{content:""}.ion-ios-clock:before{content:""}.ion-ios-clock-outline:before{content:""}.ion-ios-close:before{content:""}.ion-ios-close-empty:before{content:""}.ion-ios-close-outline:before{content:""}.ion-ios-cloud:before{content:""}.ion-ios-cloud-download:before{content:""}.ion-ios-cloud-download-outline:before{content:""}.ion-ios-cloud-outline:before{content:""}.ion-ios-cloud-upload:before{content:""}.ion-ios-cloud-upload-outline:before{content:""}.ion-ios-cloudy:before{content:""}.ion-ios-cloudy-night:before{content:""}.ion-ios-cloudy-night-outline:before{content:""}.ion-ios-cloudy-outline:before{content:""}.ion-ios-cog:before{content:""}.ion-ios-cog-outline:before{content:""}.ion-ios-color-filter:before{content:""}.ion-ios-color-filter-outline:before{content:""}.ion-ios-color-wand:before{content:""}.ion-ios-color-wand-outline:before{content:""}.ion-ios-compose:before{content:""}.ion-ios-compose-outline:before{content:""}.ion-ios-contact:before{content:""}.ion-ios-contact-outline:before{content:""}.ion-ios-copy:before{content:""}.ion-ios-copy-outline:before{content:""}.ion-ios-crop:before{content:""}.ion-ios-crop-strong:before{content:""}.ion-ios-download:before{content:""}.ion-ios-download-outline:before{content:""}.ion-ios-drag:before{content:""}.ion-ios-email:before{content:""}.ion-ios-email-outline:before{content:""}.ion-ios-eye:before{content:""}.ion-ios-eye-outline:before{content:""}.ion-ios-fastforward:before{content:""}.ion-ios-fastforward-outline:before{content:""}.ion-ios-filing:before{content:""}.ion-ios-filing-outline:before{content:""}.ion-ios-film:before{content:""}.ion-ios-film-outline:before{content:""}.ion-ios-flag:before{content:""}.ion-ios-flag-outline:before{content:""}.ion-ios-flame:before{content:""}.ion-ios-flame-outline:before{content:""}.ion-ios-flask:before{content:""}.ion-ios-flask-outline:before{content:""}.ion-ios-flower:before{content:""}.ion-ios-flower-outline:before{content:""}.ion-ios-folder:before{content:""}.ion-ios-folder-outline:before{content:""}.ion-ios-football:before{content:""}.ion-ios-football-outline:before{content:""}.ion-ios-game-controller-a:before{content:""}.ion-ios-game-controller-a-outline:before{content:""}.ion-ios-game-controller-b:before{content:""}.ion-ios-game-controller-b-outline:before{content:""}.ion-ios-gear:before{content:""}.ion-ios-gear-outline:before{content:""}.ion-ios-glasses:before{content:""}.ion-ios-glasses-outline:before{content:""}.ion-ios-grid-view:before{content:""}.ion-ios-grid-view-outline:before{content:""}.ion-ios-heart:before{content:""}.ion-ios-heart-outline:before{content:""}.ion-ios-help:before{content:""}.ion-ios-help-empty:before{content:""}.ion-ios-help-outline:before{content:""}.ion-ios-home:before{content:""}.ion-ios-home-outline:before{content:""}.ion-ios-infinite:before{content:""}.ion-ios-infinite-outline:before{content:""}.ion-ios-information:before{content:""}.ion-ios-information-empty:before{content:""}.ion-ios-information-outline:before{content:""}.ion-ios-ionic-outline:before{content:""}.ion-ios-keypad:before{content:""}.ion-ios-keypad-outline:before{content:""}.ion-ios-lightbulb:before{content:""}.ion-ios-lightbulb-outline:before{content:""}.ion-ios-list:before{content:""}.ion-ios-list-outline:before{content:""}.ion-ios-location:before{content:""}.ion-ios-location-outline:before{content:""}.ion-ios-locked:before{content:""}.ion-ios-locked-outline:before{content:""}.ion-ios-loop:before{content:""}.ion-ios-loop-strong:before{content:""}.ion-ios-medical:before{content:""}.ion-ios-medical-outline:before{content:""}.ion-ios-medkit:before{content:""}.ion-ios-medkit-outline:before{content:""}.ion-ios-mic:before{content:""}.ion-ios-mic-off:before{content:""}.ion-ios-mic-outline:before{content:""}.ion-ios-minus:before{content:""}.ion-ios-minus-empty:before{content:""}.ion-ios-minus-outline:before{content:""}.ion-ios-monitor:before{content:""}.ion-ios-monitor-outline:before{content:""}.ion-ios-moon:before{content:""}.ion-ios-moon-outline:before{content:""}.ion-ios-more:before{content:""}.ion-ios-more-outline:before{content:""}.ion-ios-musical-note:before{content:""}.ion-ios-musical-notes:before{content:""}.ion-ios-navigate:before{content:""}.ion-ios-navigate-outline:before{content:""}.ion-ios-nutrition:before{content:""}.ion-ios-nutrition-outline:before{content:""}.ion-ios-paper:before{content:""}.ion-ios-paper-outline:before{content:""}.ion-ios-paperplane:before{content:""}.ion-ios-paperplane-outline:before{content:""}.ion-ios-partlysunny:before{content:""}.ion-ios-partlysunny-outline:before{content:""}.ion-ios-pause:before{content:""}.ion-ios-pause-outline:before{content:""}.ion-ios-paw:before{content:""}.ion-ios-paw-outline:before{content:""}.ion-ios-people:before{content:""}.ion-ios-people-outline:before{content:""}.ion-ios-person:before{content:""}.ion-ios-person-outline:before{content:""}.ion-ios-personadd:before{content:""}.ion-ios-personadd-outline:before{content:""}.ion-ios-photos:before{content:""}.ion-ios-photos-outline:before{content:""}.ion-ios-pie:before{content:""}.ion-ios-pie-outline:before{content:""}.ion-ios-pint:before{content:""}.ion-ios-pint-outline:before{content:""}.ion-ios-play:before{content:""}.ion-ios-play-outline:before{content:""}.ion-ios-plus:before{content:""}.ion-ios-plus-empty:before{content:""}.ion-ios-plus-outline:before{content:""}.ion-ios-pricetag:before{content:""}.ion-ios-pricetag-outline:before{content:""}.ion-ios-pricetags:before{content:""}.ion-ios-pricetags-outline:before{content:""}.ion-ios-printer:before{content:""}.ion-ios-printer-outline:before{content:""}.ion-ios-pulse:before{content:""}.ion-ios-pulse-strong:before{content:""}.ion-ios-rainy:before{content:""}.ion-ios-rainy-outline:before{content:""}.ion-ios-recording:before{content:""}.ion-ios-recording-outline:before{content:""}.ion-ios-redo:before{content:""}.ion-ios-redo-outline:before{content:""}.ion-ios-refresh:before{content:""}.ion-ios-refresh-empty:before{content:""}.ion-ios-refresh-outline:before{content:""}.ion-ios-reload:before{content:""}.ion-ios-reverse-camera:before{content:""}.ion-ios-reverse-camera-outline:before{content:""}.ion-ios-rewind:before{content:""}.ion-ios-rewind-outline:before{content:""}.ion-ios-rose:before{content:""}.ion-ios-rose-outline:before{content:""}.ion-ios-search:before{content:""}.ion-ios-search-strong:before{content:""}.ion-ios-settings:before{content:""}.ion-ios-settings-strong:before{content:""}.ion-ios-shuffle:before{content:""}.ion-ios-shuffle-strong:before{content:""}.ion-ios-skipbackward:before{content:""}.ion-ios-skipbackward-outline:before{content:""}.ion-ios-skipforward:before{content:""}.ion-ios-skipforward-outline:before{content:""}.ion-ios-snowy:before{content:""}.ion-ios-speedometer:before{content:""}.ion-ios-speedometer-outline:before{content:""}.ion-ios-star:before{content:""}.ion-ios-star-half:before{content:""}.ion-ios-star-outline:before{content:""}.ion-ios-stopwatch:before{content:""}.ion-ios-stopwatch-outline:before{content:""}.ion-ios-sunny:before{content:""}.ion-ios-sunny-outline:before{content:""}.ion-ios-telephone:before{content:""}.ion-ios-telephone-outline:before{content:""}.ion-ios-tennisball:before{content:""}.ion-ios-tennisball-outline:before{content:""}.ion-ios-thunderstorm:before{content:""}.ion-ios-thunderstorm-outline:before{content:""}.ion-ios-time:before{content:""}.ion-ios-time-outline:before{content:""}.ion-ios-timer:before{content:""}.ion-ios-timer-outline:before{content:""}.ion-ios-toggle:before{content:""}.ion-ios-toggle-outline:before{content:""}.ion-ios-trash:before{content:""}.ion-ios-trash-outline:before{content:""}.ion-ios-undo:before{content:""}.ion-ios-undo-outline:before{content:""}.ion-ios-unlocked:before{content:""}.ion-ios-unlocked-outline:before{content:""}.ion-ios-upload:before{content:""}.ion-ios-upload-outline:before{content:""}.ion-ios-videocam:before{content:""}.ion-ios-videocam-outline:before{content:""}.ion-ios-volume-high:before{content:""}.ion-ios-volume-low:before{content:""}.ion-ios-wineglass:before{content:""}.ion-ios-wineglass-outline:before{content:""}.ion-ios-world:before{content:""}.ion-ios-world-outline:before{content:""}.ion-ipad:before{content:""}.ion-iphone:before{content:""}.ion-ipod:before{content:""}.ion-jet:before{content:""}.ion-key:before{content:""}.ion-knife:before{content:""}.ion-laptop:before{content:""}.ion-leaf:before{content:""}.ion-levels:before{content:""}.ion-lightbulb:before{content:""}.ion-link:before{content:""}.ion-load-a:before{content:""}.ion-load-b:before{content:""}.ion-load-c:before{content:""}.ion-load-d:before{content:""}.ion-location:before{content:""}.ion-lock-combination:before{content:""}.ion-locked:before{content:""}.ion-log-in:before{content:""}.ion-log-out:before{content:""}.ion-loop:before{content:""}.ion-magnet:before{content:""}.ion-male:before{content:""}.ion-man:before{content:""}.ion-map:before{content:""}.ion-medkit:before{content:""}.ion-merge:before{content:""}.ion-mic-a:before{content:""}.ion-mic-b:before{content:""}.ion-mic-c:before{content:""}.ion-minus:before{content:""}.ion-minus-circled:before{content:""}.ion-minus-round:before{content:""}.ion-model-s:before{content:""}.ion-monitor:before{content:""}.ion-more:before{content:""}.ion-mouse:before{content:""}.ion-music-note:before{content:""}.ion-navicon:before{content:""}.ion-navicon-round:before{content:""}.ion-navigate:before{content:""}.ion-network:before{content:""}.ion-no-smoking:before{content:""}.ion-nuclear:before{content:""}.ion-outlet:before{content:""}.ion-paintbrush:before{content:""}.ion-paintbucket:before{content:""}.ion-paper-airplane:before{content:""}.ion-paperclip:before{content:""}.ion-pause:before{content:""}.ion-person:before{content:""}.ion-person-add:before{content:""}.ion-person-stalker:before{content:""}.ion-pie-graph:before{content:""}.ion-pin:before{content:""}.ion-pinpoint:before{content:""}.ion-pizza:before{content:""}.ion-plane:before{content:""}.ion-planet:before{content:""}.ion-play:before{content:""}.ion-playstation:before{content:""}.ion-plus:before{content:""}.ion-plus-circled:before{content:""}.ion-plus-round:before{content:""}.ion-podium:before{content:""}.ion-pound:before{content:""}.ion-power:before{content:""}.ion-pricetag:before{content:""}.ion-pricetags:before{content:""}.ion-printer:before{content:""}.ion-pull-request:before{content:""}.ion-qr-scanner:before{content:""}.ion-quote:before{content:""}.ion-radio-waves:before{content:""}.ion-record:before{content:""}.ion-refresh:before{content:""}.ion-reply:before{content:""}.ion-reply-all:before{content:""}.ion-ribbon-a:before{content:""}.ion-ribbon-b:before{content:""}.ion-sad:before{content:""}.ion-sad-outline:before{content:""}.ion-scissors:before{content:""}.ion-search:before{content:""}.ion-settings:before{content:""}.ion-share:before{content:""}.ion-shuffle:before{content:""}.ion-skip-backward:before{content:""}.ion-skip-forward:before{content:""}.ion-social-android:before{content:""}.ion-social-android-outline:before{content:""}.ion-social-angular:before{content:""}.ion-social-angular-outline:before{content:""}.ion-social-apple:before{content:""}.ion-social-apple-outline:before{content:""}.ion-social-bitcoin:before{content:""}.ion-social-bitcoin-outline:before{content:""}.ion-social-buffer:before{content:""}.ion-social-buffer-outline:before{content:""}.ion-social-chrome:before{content:""}.ion-social-chrome-outline:before{content:""}.ion-social-codepen:before{content:""}.ion-social-codepen-outline:before{content:""}.ion-social-css3:before{content:""}.ion-social-css3-outline:before{content:""}.ion-social-designernews:before{content:""}.ion-social-designernews-outline:before{content:""}.ion-social-dribbble:before{content:""}.ion-social-dribbble-outline:before{content:""}.ion-social-dropbox:before{content:""}.ion-social-dropbox-outline:before{content:""}.ion-social-euro:before{content:""}.ion-social-euro-outline:before{content:""}.ion-social-facebook:before{content:""}.ion-social-facebook-outline:before{content:""}.ion-social-foursquare:before{content:""}.ion-social-foursquare-outline:before{content:""}.ion-social-freebsd-devil:before{content:""}.ion-social-github:before{content:""}.ion-social-github-outline:before{content:""}.ion-social-google:before{content:""}.ion-social-google-outline:before{content:""}.ion-social-googleplus:before{content:""}.ion-social-googleplus-outline:before{content:""}.ion-social-hackernews:before{content:""}.ion-social-hackernews-outline:before{content:""}.ion-social-html5:before{content:""}.ion-social-html5-outline:before{content:""}.ion-social-instagram:before{content:""}.ion-social-instagram-outline:before{content:""}.ion-social-javascript:before{content:""}.ion-social-javascript-outline:before{content:""}.ion-social-linkedin:before{content:""}.ion-social-linkedin-outline:before{content:""}.ion-social-markdown:before{content:""}.ion-social-nodejs:before{content:""}.ion-social-octocat:before{content:""}.ion-social-pinterest:before{content:""}.ion-social-pinterest-outline:before{content:""}.ion-social-python:before{content:""}.ion-social-reddit:before{content:""}.ion-social-reddit-outline:before{content:""}.ion-social-rss:before{content:""}.ion-social-rss-outline:before{content:""}.ion-social-sass:before{content:""}.ion-social-skype:before{content:""}.ion-social-skype-outline:before{content:""}.ion-social-snapchat:before{content:""}.ion-social-snapchat-outline:before{content:""}.ion-social-tumblr:before{content:""}.ion-social-tumblr-outline:before{content:""}.ion-social-tux:before{content:""}.ion-social-twitch:before{content:""}.ion-social-twitch-outline:before{content:""}.ion-social-twitter:before{content:""}.ion-social-twitter-outline:before{content:""}.ion-social-usd:before{content:""}.ion-social-usd-outline:before{content:""}.ion-social-vimeo:before{content:""}.ion-social-vimeo-outline:before{content:""}.ion-social-whatsapp:before{content:""}.ion-social-whatsapp-outline:before{content:""}.ion-social-windows:before{content:""}.ion-social-windows-outline:before{content:""}.ion-social-wordpress:before{content:""}.ion-social-wordpress-outline:before{content:""}.ion-social-yahoo:before{content:""}.ion-social-yahoo-outline:before{content:""}.ion-social-yen:before{content:""}.ion-social-yen-outline:before{content:""}.ion-social-youtube:before{content:""}.ion-social-youtube-outline:before{content:""}.ion-soup-can:before{content:""}.ion-soup-can-outline:before{content:""}.ion-speakerphone:before{content:""}.ion-speedometer:before{content:""}.ion-spoon:before{content:""}.ion-star:before{content:""}.ion-stats-bars:before{content:""}.ion-steam:before{content:""}.ion-stop:before{content:""}.ion-thermometer:before{content:""}.ion-thumbsdown:before{content:""}.ion-thumbsup:before{content:""}.ion-toggle:before{content:""}.ion-toggle-filled:before{content:""}.ion-transgender:before{content:""}.ion-trash-a:before{content:""}.ion-trash-b:before{content:""}.ion-trophy:before{content:""}.ion-tshirt:before{content:""}.ion-tshirt-outline:before{content:""}.ion-umbrella:before{content:""}.ion-university:before{content:""}.ion-unlocked:before{content:""}.ion-upload:before{content:""}.ion-usb:before{content:""}.ion-videocamera:before{content:""}.ion-volume-high:before{content:""}.ion-volume-low:before{content:""}.ion-volume-medium:before{content:""}.ion-volume-mute:before{content:""}.ion-wand:before{content:""}.ion-waterdrop:before{content:""}.ion-wifi:before{content:""}.ion-wineglass:before{content:""}.ion-woman:before{content:""}.ion-wrench:before{content:""}.ion-xbox:before{content:""}a,abbr,acronym,address,applet,article,aside,audio,b,big,blockquote,body,canvas,caption,center,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,i,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,output,p,pre,q,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,u,ul,var,video{margin:0;padding:0;border:0;vertical-align:baseline;font:inherit;font-size:100%}ol,ul{list-style:none}blockquote,q{quotes:none}audio:not([controls]){display:none;height:0}[hidden],template{display:none}script{display:none!important}html{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}:focus,a,a:active,a:focus,a:hover,button,button:focus{outline:0}a{-webkit-user-drag:none;-webkit-tap-highlight-color:transparent;-webkit-tap-highlight-color:transparent}a[href]:hover{cursor:pointer}b,strong{font-weight:700}dfn{font-style:italic}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}code,kbd,pre,samp{font-size:1em;font-family:monospace,serif}pre{white-space:pre-wrap}q{quotes:"\201C" "\201D" "\2018" "\2019"}sub,sup{position:relative;vertical-align:baseline;font-size:75%;line-height:0}sup{top:-.5em}sub{bottom:-.25em}fieldset{margin:0 2px;padding:.35em .625em .75em;border:1px solid silver}button,input,select,textarea{margin:0;outline-offset:0;outline-style:none;outline-width:0;-webkit-font-smoothing:inherit;background-image:none}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button}button[disabled],html input[disabled]{cursor:default}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}textarea{overflow:auto}img{-webkit-user-drag:none}table{border-spacing:0;border-collapse:collapse}*,:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{overflow:hidden;-ms-touch-action:pan-y;touch-action:pan-y}.ionic-body,body{-webkit-touch-callout:none;-webkit-font-smoothing:antialiased;font-smoothing:antialiased;-webkit-text-size-adjust:none;-moz-text-size-adjust:none;text-size-adjust:none;-webkit-tap-highlight-color:transparent;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;top:0;right:0;bottom:0;left:0;overflow:hidden;margin:0;padding:0;color:#000;word-wrap:break-word;font-size:14px;font-family:-apple-system;font-family:"-apple-system","Helvetica Neue",Roboto,"Segoe UI",sans-serif;line-height:20px;text-rendering:optimizeLegibility;-webkit-backface-visibility:hidden;-webkit-user-drag:none;-ms-content-zooming:none}body.grade-b,body.grade-c{text-rendering:auto}.content{position:relative}.scroll-content{position:absolute;top:0;right:0;bottom:0;left:0;overflow:hidden;margin-top:-1px;padding-top:1px;margin-bottom:-1px;width:auto;height:auto}.menu .scroll-content.scroll-content-false{z-index:11}.scroll-view{position:relative;display:block;overflow:hidden;margin-top:-1px}.scroll-view.overflow-scroll{position:relative}.scroll-view.scroll-x{overflow-x:scroll;overflow-y:hidden}.scroll-view.scroll-y{overflow-x:hidden;overflow-y:scroll}.scroll-view.scroll-xy{overflow-x:scroll;overflow-y:scroll}.scroll{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-touch-callout:none;-webkit-text-size-adjust:none;-moz-text-size-adjust:none;text-size-adjust:none;-webkit-transform-origin:left top;transform-origin:left top}@-ms-viewport{width:device-width}.scroll-bar{position:absolute;z-index:9999}.ng-animate .scroll-bar{visibility:hidden}.scroll-bar-h{right:2px;bottom:3px;left:2px;height:3px}.scroll-bar-h .scroll-bar-indicator{height:100%}.scroll-bar-v{top:2px;right:3px;bottom:2px;width:3px}.scroll-bar-v .scroll-bar-indicator{width:100%}.scroll-bar-indicator{position:absolute;border-radius:4px;background:rgba(0,0,0,.3);opacity:1;-webkit-transition:opacity .3s linear;transition:opacity .3s linear}.scroll-bar-indicator.scroll-bar-fade-out{opacity:0}.platform-android .scroll-bar-indicator{border-radius:0}.grade-b .scroll-bar-indicator,.grade-c .scroll-bar-indicator{background:#aaa}.grade-b .scroll-bar-indicator.scroll-bar-fade-out,.grade-c .scroll-bar-indicator.scroll-bar-fade-out{-webkit-transition:none;transition:none}ion-infinite-scroll{height:60px;width:100%;display:block;display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-moz-flex;display:-ms-flexbox;display:flex;-webkit-box-direction:normal;-webkit-box-orient:horizontal;-webkit-flex-direction:row;-moz-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;-moz-justify-content:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;-webkit-align-items:center;-moz-align-items:center;align-items:center}ion-infinite-scroll .icon{font-size:30px;color:#666}ion-infinite-scroll:not(.active) .icon:before,ion-infinite-scroll:not(.active) .spinner{display:none}.overflow-scroll{overflow-x:hidden;overflow-y:scroll;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar;top:0;right:0;bottom:0;left:0;position:absolute}.overflow-scroll.pane{overflow-x:hidden;overflow-y:scroll}.overflow-scroll .scroll{position:static;height:100%;-webkit-transform:translate3d(0,0,0)}.has-header{top:44px}.no-header{top:0}.has-subheader{top:88px}.has-tabs-top{top:93px}.has-header.has-subheader.has-tabs-top{top:137px}.has-footer{bottom:44px}.has-subfooter{bottom:88px}.bar-footer.has-tabs,.has-tabs{bottom:49px}.bar-footer.has-tabs.pane,.has-tabs.pane{bottom:49px;height:auto}.bar-subfooter.has-tabs,.has-footer.has-tabs{bottom:93px}.pane{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);-webkit-transition-duration:0;transition-duration:0;z-index:1}.view{z-index:1}.pane,.view{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;background-color:#fff;overflow:hidden}.view-container{position:absolute;display:block;width:100%;height:100%}p{margin:0 0 10px}small{font-size:85%}cite{font-style:normal}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{color:#000;font-weight:500;font-family:"-apple-system","Helvetica Neue",Roboto,"Segoe UI",sans-serif;line-height:1.2}.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:400;line-height:1}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1:first-child,.h2:first-child,.h3:first-child,h1:first-child,h2:first-child,h3:first-child{margin-top:0}.h1+.h1,.h1+.h2,.h1+.h3,.h1+h1,.h1+h2,.h1+h3,.h2+.h1,.h2+.h2,.h2+.h3,.h2+h1,.h2+h2,.h2+h3,.h3+.h1,.h3+.h2,.h3+.h3,.h3+h1,.h3+h2,.h3+h3,h1+.h1,h1+.h2,h1+.h3,h1+h1,h1+h2,h1+h3,h2+.h1,h2+.h2,h2+.h3,h2+h1,h2+h2,h2+h3,h3+.h1,h3+.h2,h3+.h3,h3+h1,h3+h2,h3+h3{margin-top:10px}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}.h1 small,h1 small{font-size:24px}.h2 small,h2 small{font-size:18px}.h3 small,.h4 small,h3 small,h4 small{font-size:14px}dl{margin-bottom:20px}dd,dt{line-height:1.42857}dt{font-weight:700}blockquote{margin:0 0 20px;padding:10px 20px;border-left:5px solid gray}blockquote p{font-weight:300;font-size:17.5px;line-height:1.25}blockquote p:last-child{margin-bottom:0}blockquote small{display:block;line-height:1.42857}blockquote small:before{content:'\2014 \00A0'}blockquote:after,blockquote:before,q:after,q:before{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:1.42857}a{color:#387ef5}a.subdued{padding-right:10px;color:#888;text-decoration:none}a.subdued:hover{text-decoration:none}a.subdued:last-child{padding-right:0}.action-sheet-backdrop{-webkit-transition:background-color 150ms ease-in-out;transition:background-color 150ms ease-in-out;position:fixed;top:0;left:0;z-index:11;width:100%;height:100%;background-color:transparent}.action-sheet-backdrop.active{background-color:rgba(0,0,0,.4)}.action-sheet-wrapper{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0);-webkit-transition:all cubic-bezier(.36,.66,.04,1) 500ms;transition:all cubic-bezier(.36,.66,.04,1) 500ms;position:absolute;bottom:0;left:0;right:0;width:100%;max-width:500px;margin:auto}.action-sheet-up{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.action-sheet{margin-left:8px;margin-right:8px;width:auto;z-index:11;overflow:hidden}.action-sheet .button{display:block;padding:1px;width:100%;border-radius:0;border-color:#d1d3d6;background-color:transparent;color:#007aff;font-size:21px}.action-sheet .button:hover{color:#007aff}.action-sheet .button.destructive,.action-sheet .button.destructive:hover{color:#ff3b30}.action-sheet .button.activated,.action-sheet .button.active{box-shadow:none;border-color:#d1d3d6;color:#007aff;background:#e4e5e7}.action-sheet-has-icons .icon{position:absolute;left:16px}.action-sheet-title{padding:16px;color:#8f8f8f;text-align:center;font-size:13px}.action-sheet-group{margin-bottom:8px;border-radius:4px;background-color:#fff;overflow:hidden}.action-sheet-group .button{border-width:1px 0 0 0}.action-sheet-group .button:first-child:last-child{border-width:0}.action-sheet-options{background:#f1f2f3}.action-sheet-cancel .button{font-weight:500}.action-sheet-open,.action-sheet-open.modal-open .modal{pointer-events:none}.action-sheet-open .action-sheet-backdrop{pointer-events:auto}.platform-android .action-sheet-backdrop.active{background-color:rgba(0,0,0,.2)}.platform-android .action-sheet{margin:0}.platform-android .action-sheet .action-sheet-title,.platform-android .action-sheet .button{text-align:left;border-color:transparent;font-size:16px;color:inherit}.platform-android .action-sheet .action-sheet-title{font-size:14px;padding:16px;color:#666}.platform-android .action-sheet .button.activated,.platform-android .action-sheet .button.active{background:#e8e8e8}.platform-android .action-sheet-group{margin:0;border-radius:0;background-color:#fafafa}.platform-android .action-sheet-cancel{display:none}.platform-android .action-sheet-has-icons .button{padding-left:56px}.backdrop{position:fixed;top:0;left:0;z-index:11;width:100%;height:100%;background-color:rgba(0,0,0,.4);visibility:hidden;opacity:0;-webkit-transition:.1s opacity linear;transition:.1s opacity linear}.backdrop.visible{visibility:visible}.backdrop.active{opacity:1}.bar{display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-moz-flex;display:-ms-flexbox;display:flex;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;position:absolute;right:0;left:0;z-index:9;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:5px;width:100%;height:44px;border-width:0;border-style:solid;border-top:1px solid transparent;border-bottom:1px solid #ddd;background-color:#fff;background-size:0}@media (min--moz-device-pixel-ratio:1.5),(-webkit-min-device-pixel-ratio:1.5),(min-device-pixel-ratio:1.5),(min-resolution:144dpi),(min-resolution:1.5dppx){.bar{border:none;background-image:linear-gradient(0deg,#ddd,#ddd 50%,transparent 50%);background-position:bottom;background-size:100% 1px;background-repeat:no-repeat}}.bar.bar-clear{border:none;background:0 0;color:#fff}.bar.bar-clear .button,.bar.bar-clear .title{color:#fff}.bar.item-input-inset .item-input-wrapper{margin-top:-1px}.bar.item-input-inset .item-input-wrapper input{padding-left:8px;width:94%;height:28px;background:0 0}.bar.bar-light{border-color:#ddd;background-color:#fff;background-image:linear-gradient(0deg,#ddd,#ddd 50%,transparent 50%);color:#444}.bar.bar-light .title{color:#444}.bar.bar-light.bar-footer{background-image:linear-gradient(180deg,#ddd,#ddd 50%,transparent 50%)}.bar.bar-stable{border-color:#b2b2b2;background-color:#f8f8f8;background-image:linear-gradient(0deg,#b2b2b2,#b2b2b2 50%,transparent 50%);color:#444}.bar.bar-stable .title{color:#444}.bar.bar-stable.bar-footer{background-image:linear-gradient(180deg,#b2b2b2,#b2b2b2 50%,transparent 50%)}.bar.bar-positive{border-color:#0c60ee;background-color:#387ef5;background-image:linear-gradient(0deg,#0c60ee,#0c60ee 50%,transparent 50%);color:#fff}.bar.bar-positive .title{color:#fff}.bar.bar-positive.bar-footer{background-image:linear-gradient(180deg,#0c60ee,#0c60ee 50%,transparent 50%)}.bar.bar-calm{border-color:#0a9dc7;background-color:#11c1f3;background-image:linear-gradient(0deg,#0a9dc7,#0a9dc7 50%,transparent 50%);color:#fff}.bar.bar-calm .title{color:#fff}.bar.bar-calm.bar-footer{background-image:linear-gradient(180deg,#0a9dc7,#0a9dc7 50%,transparent 50%)}.bar.bar-assertive{border-color:#e42112;background-color:#ef473a;background-image:linear-gradient(0deg,#e42112,#e42112 50%,transparent 50%);color:#fff}.bar.bar-assertive .title{color:#fff}.bar.bar-assertive.bar-footer{background-image:linear-gradient(180deg,#e42112,#e42112 50%,transparent 50%)}.bar.bar-balanced{border-color:#28a54c;background-color:#33cd5f;background-image:linear-gradient(0deg,#28a54c,#28a54c 50%,transparent 50%);color:#fff}.bar.bar-balanced .title{color:#fff}.bar.bar-balanced.bar-footer{background-image:linear-gradient(180deg,#28a54c,#28a54c 50%,transparent 50%)}.bar.bar-energized{border-color:#e6b500;background-color:#ffc900;background-image:linear-gradient(0deg,#e6b500,#e6b500 50%,transparent 50%);color:#fff}.bar.bar-energized .title{color:#fff}.bar.bar-energized.bar-footer{background-image:linear-gradient(180deg,#e6b500,#e6b500 50%,transparent 50%)}.bar.bar-royal{border-color:#6b46e5;background-color:#886aea;background-image:linear-gradient(0deg,#6b46e5,#6b46e5 50%,transparent 50%);color:#fff}.bar.bar-royal .title{color:#fff}.bar.bar-royal.bar-footer{background-image:linear-gradient(180deg,#6b46e5,#6b46e5 50%,transparent 50%)}.bar.bar-dark{border-color:#111;background-color:#444;background-image:linear-gradient(0deg,#111,#111 50%,transparent 50%);color:#fff}.bar.bar-dark .title{color:#fff}.bar.bar-dark.bar-footer{background-image:linear-gradient(180deg,#111,#111 50%,transparent 50%)}.bar .title{display:block;position:absolute;top:0;right:0;left:0;z-index:0;overflow:hidden;margin:0 10px;min-width:30px;height:43px;text-align:center;text-overflow:ellipsis;white-space:nowrap;font-size:17px;font-weight:500;line-height:44px}.bar .title.title-left{text-align:left}.bar .title.title-right{text-align:right}.bar .title a{color:inherit}.bar .button,.bar button{z-index:1;padding:0 8px;min-width:initial;min-height:31px;font-weight:400;font-size:13px;line-height:32px}.bar .button .icon:before,.bar .button.button-icon:before,.bar .button.icon-left:before,.bar .button.icon-right:before,.bar .button.icon:before,.bar button .icon:before,.bar button.button-icon:before,.bar button.icon-left:before,.bar button.icon-right:before,.bar button.icon:before{padding-right:2px;padding-left:2px;font-size:20px;line-height:32px}.bar .button.button-icon,.bar button.button-icon{font-size:17px}.bar .button.button-icon .icon:before,.bar .button.button-icon.icon-left:before,.bar .button.button-icon.icon-right:before,.bar .button.button-icon:before,.bar button.button-icon .icon:before,.bar button.button-icon.icon-left:before,.bar button.button-icon.icon-right:before,.bar button.button-icon:before{vertical-align:top;font-size:32px;line-height:32px}.bar .button.button-clear,.bar button.button-clear{padding-right:2px;padding-left:2px;font-weight:300;font-size:17px}.bar .button.button-clear .icon:before,.bar .button.button-clear.icon-left:before,.bar .button.button-clear.icon-right:before,.bar .button.button-clear.icon:before,.bar button.button-clear .icon:before,.bar button.button-clear.icon-left:before,.bar button.button-clear.icon-right:before,.bar button.button-clear.icon:before{font-size:32px;line-height:32px}.bar .button.back-button,.bar button.back-button{display:block;margin-right:5px;padding:0;white-space:nowrap;font-weight:400}.bar .button.back-button.activated,.bar .button.back-button.active,.bar button.back-button.activated,.bar button.back-button.active{opacity:.2}.bar .button-bar>.button,.bar .buttons>.button{min-height:31px;line-height:32px}.bar .button+.button-bar,.bar .button-bar+.button{margin-left:5px}.bar .buttons,.bar .buttons.primary-buttons,.bar .buttons.secondary-buttons{display:inherit}.bar .buttons span{display:inline-block}.bar .buttons-left span{margin-right:5px;display:inherit}.bar .buttons-right span{margin-left:5px;display:inherit}.bar .buttons.pull-right,.bar .title+.button:last-child,.bar .title+.buttons,.bar>.button+.button:last-child,.bar>.button.pull-right{position:absolute;top:5px;right:5px;bottom:5px}.platform-android .nav-bar-has-subheader .bar{background-image:none}.platform-android .bar .back-button .icon:before{font-size:24px}.platform-android .bar .title{font-size:19px;line-height:44px}.bar-light .button{border-color:#ddd;background-color:#fff;color:#444}.bar-light .button:hover{color:#444;text-decoration:none}.bar-light .button.activated,.bar-light .button.active{border-color:#ccc;background-color:#fafafa}.bar-light .button.button-clear{border-color:transparent;background:0 0;box-shadow:none;color:#444;font-size:17px}.bar-light .button.button-icon{border-color:transparent;background:0 0}.bar-stable .button{border-color:#b2b2b2;background-color:#f8f8f8;color:#444}.bar-stable .button:hover{color:#444;text-decoration:none}.bar-stable .button.activated,.bar-stable .button.active{border-color:#a2a2a2;background-color:#e5e5e5}.bar-stable .button.button-clear{border-color:transparent;background:0 0;box-shadow:none;color:#444;font-size:17px}.bar-stable .button.button-icon{border-color:transparent;background:0 0}.bar-positive .button{border-color:#0c60ee;background-color:#387ef5;color:#fff}.bar-positive .button:hover{color:#fff;text-decoration:none}.bar-positive .button.activated,.bar-positive .button.active{border-color:#0c60ee;background-color:#0c60ee}.bar-positive .button.button-clear{border-color:transparent;background:0 0;box-shadow:none;color:#fff;font-size:17px}.bar-positive .button.button-icon{border-color:transparent;background:0 0}.bar-calm .button{border-color:#0a9dc7;background-color:#11c1f3;color:#fff}.bar-calm .button:hover{color:#fff;text-decoration:none}.bar-calm .button.activated,.bar-calm .button.active{border-color:#0a9dc7;background-color:#0a9dc7}.bar-calm .button.button-clear{border-color:transparent;background:0 0;box-shadow:none;color:#fff;font-size:17px}.bar-calm .button.button-icon{border-color:transparent;background:0 0}.bar-assertive .button{border-color:#e42112;background-color:#ef473a;color:#fff}.bar-assertive .button:hover{color:#fff;text-decoration:none}.bar-assertive .button.activated,.bar-assertive .button.active{border-color:#e42112;background-color:#e42112}.bar-assertive .button.button-clear{border-color:transparent;background:0 0;box-shadow:none;color:#fff;font-size:17px}.bar-assertive .button.button-icon{border-color:transparent;background:0 0}.bar-balanced .button{border-color:#28a54c;background-color:#33cd5f;color:#fff}.bar-balanced .button:hover{color:#fff;text-decoration:none}.bar-balanced .button.activated,.bar-balanced .button.active{border-color:#28a54c;background-color:#28a54c}.bar-balanced .button.button-clear{border-color:transparent;background:0 0;box-shadow:none;color:#fff;font-size:17px}.bar-balanced .button.button-icon{border-color:transparent;background:0 0}.bar-energized .button{border-color:#e6b500;background-color:#ffc900;color:#fff}.bar-energized .button:hover{color:#fff;text-decoration:none}.bar-energized .button.activated,.bar-energized .button.active{border-color:#e6b500;background-color:#e6b500}.bar-energized .button.button-clear{border-color:transparent;background:0 0;box-shadow:none;color:#fff;font-size:17px}.bar-energized .button.button-icon{border-color:transparent;background:0 0}.bar-royal .button{border-color:#6b46e5;background-color:#886aea;color:#fff}.bar-royal .button:hover{color:#fff;text-decoration:none}.bar-royal .button.activated,.bar-royal .button.active{border-color:#6b46e5;background-color:#6b46e5}.bar-royal .button.button-clear{border-color:transparent;background:0 0;box-shadow:none;color:#fff;font-size:17px}.bar-royal .button.button-icon{border-color:transparent;background:0 0}.bar-dark .button{border-color:#111;background-color:#444;color:#fff}.bar-dark .button:hover{color:#fff;text-decoration:none}.bar-dark .button.activated,.bar-dark .button.active{border-color:#000;background-color:#262626}.bar-dark .button.button-clear{border-color:transparent;background:0 0;box-shadow:none;color:#fff;font-size:17px}.bar-dark .button.button-icon{border-color:transparent;background:0 0}.bar-header{top:0;border-top-width:0;border-bottom-width:1px}.bar-header.has-tabs-top,.tabs-top .bar-header{border-bottom-width:0;background-image:none}.bar-footer{bottom:0;border-top-width:1px;border-bottom-width:0;background-position:top;height:44px}.bar-footer.item-input-inset{position:absolute}.bar-footer .title{height:43px;line-height:44px}.bar-tabs{padding:0}.bar-subheader{top:44px;height:44px}.bar-subheader .title{height:43px;line-height:44px}.bar-subfooter{bottom:44px;height:44px}.bar-subfooter .title{height:43px;line-height:44px}.nav-bar-block{position:absolute;top:0;right:0;left:0;z-index:9}.bar .back-button.hide,.bar .buttons .hide{display:none}.nav-bar-tabs-top .bar{background-image:none}.tabs{display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-moz-flex;display:-ms-flexbox;display:flex;-webkit-box-direction:normal;-webkit-box-orient:horizontal;-webkit-flex-direction:horizontal;-moz-flex-direction:horizontal;-ms-flex-direction:horizontal;flex-direction:horizontal;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;-moz-justify-content:center;justify-content:center;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);border-color:#b2b2b2;background-color:#f8f8f8;background-image:linear-gradient(0deg,#b2b2b2,#b2b2b2 50%,transparent 50%);color:#444;position:absolute;bottom:0;z-index:5;width:100%;height:49px;border-style:solid;border-top-width:1px;background-size:0;line-height:49px}.tabs .tab-item .badge{background-color:#444;color:#f8f8f8}@media (min--moz-device-pixel-ratio:1.5),(-webkit-min-device-pixel-ratio:1.5),(min-device-pixel-ratio:1.5),(min-resolution:144dpi),(min-resolution:1.5dppx){.tabs{padding-top:2px;border-top:none!important;border-bottom:none;background-position:top;background-size:100% 1px;background-repeat:no-repeat}}.tabs-light>.tabs,.tabs.tabs-light{border-color:#ddd;background-color:#fff;background-image:linear-gradient(0deg,#ddd,#ddd 50%,transparent 50%);color:#444}.tabs-light>.tabs .tab-item .badge,.tabs.tabs-light .tab-item .badge{background-color:#444;color:#fff}.tabs-stable>.tabs,.tabs.tabs-stable{border-color:#b2b2b2;background-color:#f8f8f8;background-image:linear-gradient(0deg,#b2b2b2,#b2b2b2 50%,transparent 50%);color:#444}.tabs-stable>.tabs .tab-item .badge,.tabs.tabs-stable .tab-item .badge{background-color:#444;color:#f8f8f8}.tabs-positive>.tabs,.tabs.tabs-positive{border-color:#0c60ee;background-color:#387ef5;background-image:linear-gradient(0deg,#0c60ee,#0c60ee 50%,transparent 50%);color:#fff}.tabs-positive>.tabs .tab-item .badge,.tabs.tabs-positive .tab-item .badge{background-color:#fff;color:#387ef5}.tabs-calm>.tabs,.tabs.tabs-calm{border-color:#0a9dc7;background-color:#11c1f3;background-image:linear-gradient(0deg,#0a9dc7,#0a9dc7 50%,transparent 50%);color:#fff}.tabs-calm>.tabs .tab-item .badge,.tabs.tabs-calm .tab-item .badge{background-color:#fff;color:#11c1f3}.tabs-assertive>.tabs,.tabs.tabs-assertive{border-color:#e42112;background-color:#ef473a;background-image:linear-gradient(0deg,#e42112,#e42112 50%,transparent 50%);color:#fff}.tabs-assertive>.tabs .tab-item .badge,.tabs.tabs-assertive .tab-item .badge{background-color:#fff;color:#ef473a}.tabs-balanced>.tabs,.tabs.tabs-balanced{border-color:#28a54c;background-color:#33cd5f;background-image:linear-gradient(0deg,#28a54c,#28a54c 50%,transparent 50%);color:#fff}.tabs-balanced>.tabs .tab-item .badge,.tabs.tabs-balanced .tab-item .badge{background-color:#fff;color:#33cd5f}.tabs-energized>.tabs,.tabs.tabs-energized{border-color:#e6b500;background-color:#ffc900;background-image:linear-gradient(0deg,#e6b500,#e6b500 50%,transparent 50%);color:#fff}.tabs-energized>.tabs .tab-item .badge,.tabs.tabs-energized .tab-item .badge{background-color:#fff;color:#ffc900}.tabs-royal>.tabs,.tabs.tabs-royal{border-color:#6b46e5;background-color:#886aea;background-image:linear-gradient(0deg,#6b46e5,#6b46e5 50%,transparent 50%);color:#fff}.tabs-royal>.tabs .tab-item .badge,.tabs.tabs-royal .tab-item .badge{background-color:#fff;color:#886aea}.tabs-dark>.tabs,.tabs.tabs-dark{border-color:#111;background-color:#444;background-image:linear-gradient(0deg,#111,#111 50%,transparent 50%);color:#fff}.tabs-dark>.tabs .tab-item .badge,.tabs.tabs-dark .tab-item .badge{background-color:#fff;color:#444}.tabs-striped .tabs{background-color:#fff;background-image:none;border:none;border-bottom:1px solid #ddd;padding-top:2px}.tabs-striped .tab-item.activated,.tabs-striped .tab-item.active,.tabs-striped .tab-item.tab-item-active{margin-top:-2px;border-style:solid;border-width:2px 0 0 0;border-color:#444}.tabs-striped .tab-item.activated .badge,.tabs-striped .tab-item.active .badge,.tabs-striped .tab-item.tab-item-active .badge{top:2px;opacity:1}.tabs-striped.tabs-light .tabs{background-color:#fff}.tabs-striped.tabs-light .tab-item{color:rgba(68,68,68,.4);opacity:1}.tabs-striped.tabs-light .tab-item .badge{opacity:.4}.tabs-striped.tabs-light .tab-item.activated,.tabs-striped.tabs-light .tab-item.active,.tabs-striped.tabs-light .tab-item.tab-item-active{margin-top:-2px;color:#444;border-style:solid;border-width:2px 0 0 0;border-color:#444}.tabs-striped.tabs-stable .tabs{background-color:#f8f8f8}.tabs-striped.tabs-stable .tab-item{color:rgba(68,68,68,.4);opacity:1}.tabs-striped.tabs-stable .tab-item .badge{opacity:.4}.tabs-striped.tabs-stable .tab-item.activated,.tabs-striped.tabs-stable .tab-item.active,.tabs-striped.tabs-stable .tab-item.tab-item-active{margin-top:-2px;color:#444;border-style:solid;border-width:2px 0 0 0;border-color:#444}.tabs-striped.tabs-positive .tabs{background-color:#387ef5}.tabs-striped.tabs-positive .tab-item{color:rgba(255,255,255,.4);opacity:1}.tabs-striped.tabs-positive .tab-item .badge{opacity:.4}.tabs-striped.tabs-positive .tab-item.activated,.tabs-striped.tabs-positive .tab-item.active,.tabs-striped.tabs-positive .tab-item.tab-item-active{margin-top:-2px;color:#fff;border-style:solid;border-width:2px 0 0 0;border-color:#fff}.tabs-striped.tabs-calm .tabs{background-color:#11c1f3}.tabs-striped.tabs-calm .tab-item{color:rgba(255,255,255,.4);opacity:1}.tabs-striped.tabs-calm .tab-item .badge{opacity:.4}.tabs-striped.tabs-calm .tab-item.activated,.tabs-striped.tabs-calm .tab-item.active,.tabs-striped.tabs-calm .tab-item.tab-item-active{margin-top:-2px;color:#fff;border-style:solid;border-width:2px 0 0 0;border-color:#fff}.tabs-striped.tabs-assertive .tabs{background-color:#ef473a}.tabs-striped.tabs-assertive .tab-item{color:rgba(255,255,255,.4);opacity:1}.tabs-striped.tabs-assertive .tab-item .badge{opacity:.4}.tabs-striped.tabs-assertive .tab-item.activated,.tabs-striped.tabs-assertive .tab-item.active,.tabs-striped.tabs-assertive .tab-item.tab-item-active{margin-top:-2px;color:#fff;border-style:solid;border-width:2px 0 0 0;border-color:#fff}.tabs-striped.tabs-balanced .tabs{background-color:#33cd5f}.tabs-striped.tabs-balanced .tab-item{color:rgba(255,255,255,.4);opacity:1}.tabs-striped.tabs-balanced .tab-item .badge{opacity:.4}.tabs-striped.tabs-balanced .tab-item.activated,.tabs-striped.tabs-balanced .tab-item.active,.tabs-striped.tabs-balanced .tab-item.tab-item-active{margin-top:-2px;color:#fff;border-style:solid;border-width:2px 0 0 0;border-color:#fff}.tabs-striped.tabs-energized .tabs{background-color:#ffc900}.tabs-striped.tabs-energized .tab-item{color:rgba(255,255,255,.4);opacity:1}.tabs-striped.tabs-energized .tab-item .badge{opacity:.4}.tabs-striped.tabs-energized .tab-item.activated,.tabs-striped.tabs-energized .tab-item.active,.tabs-striped.tabs-energized .tab-item.tab-item-active{margin-top:-2px;color:#fff;border-style:solid;border-width:2px 0 0 0;border-color:#fff}.tabs-striped.tabs-royal .tabs{background-color:#886aea}.tabs-striped.tabs-royal .tab-item{color:rgba(255,255,255,.4);opacity:1}.tabs-striped.tabs-royal .tab-item .badge{opacity:.4}.tabs-striped.tabs-royal .tab-item.activated,.tabs-striped.tabs-royal .tab-item.active,.tabs-striped.tabs-royal .tab-item.tab-item-active{margin-top:-2px;color:#fff;border-style:solid;border-width:2px 0 0 0;border-color:#fff}.tabs-striped.tabs-dark .tabs{background-color:#444}.tabs-striped.tabs-dark .tab-item{color:rgba(255,255,255,.4);opacity:1}.tabs-striped.tabs-dark .tab-item .badge{opacity:.4}.tabs-striped.tabs-dark .tab-item.activated,.tabs-striped.tabs-dark .tab-item.active,.tabs-striped.tabs-dark .tab-item.tab-item-active{margin-top:-2px;color:#fff;border-style:solid;border-width:2px 0 0 0;border-color:#fff}.tabs-striped.tabs-top .tab-item.activated .badge,.tabs-striped.tabs-top .tab-item.active .badge,.tabs-striped.tabs-top .tab-item.tab-item-active .badge{top:4%}.tabs-striped.tabs-background-light .tabs{background-color:#fff;background-image:none}.tabs-striped.tabs-background-stable .tabs{background-color:#f8f8f8;background-image:none}.tabs-striped.tabs-background-positive .tabs{background-color:#387ef5;background-image:none}.tabs-striped.tabs-background-calm .tabs{background-color:#11c1f3;background-image:none}.tabs-striped.tabs-background-assertive .tabs{background-color:#ef473a;background-image:none}.tabs-striped.tabs-background-balanced .tabs{background-color:#33cd5f;background-image:none}.tabs-striped.tabs-background-energized .tabs{background-color:#ffc900;background-image:none}.tabs-striped.tabs-background-royal .tabs{background-color:#886aea;background-image:none}.tabs-striped.tabs-background-dark .tabs{background-color:#444;background-image:none}.tabs-striped.tabs-color-light .tab-item{color:rgba(255,255,255,.4);opacity:1}.tabs-striped.tabs-color-light .tab-item .badge{opacity:.4}.tabs-striped.tabs-color-light .tab-item.activated,.tabs-striped.tabs-color-light .tab-item.active,.tabs-striped.tabs-color-light .tab-item.tab-item-active{margin-top:-2px;color:#fff;border:0 solid #fff;border-top-width:2px}.tabs-striped.tabs-color-light .tab-item.activated .badge,.tabs-striped.tabs-color-light .tab-item.active .badge,.tabs-striped.tabs-color-light .tab-item.tab-item-active .badge{top:2px;opacity:1}.tabs-striped.tabs-color-stable .tab-item{color:rgba(248,248,248,.4);opacity:1}.tabs-striped.tabs-color-stable .tab-item .badge{opacity:.4}.tabs-striped.tabs-color-stable .tab-item.activated,.tabs-striped.tabs-color-stable .tab-item.active,.tabs-striped.tabs-color-stable .tab-item.tab-item-active{margin-top:-2px;color:#f8f8f8;border:0 solid #f8f8f8;border-top-width:2px}.tabs-striped.tabs-color-stable .tab-item.activated .badge,.tabs-striped.tabs-color-stable .tab-item.active .badge,.tabs-striped.tabs-color-stable .tab-item.tab-item-active .badge{top:2px;opacity:1}.tabs-striped.tabs-color-positive .tab-item{color:rgba(56,126,245,.4);opacity:1}.tabs-striped.tabs-color-positive .tab-item .badge{opacity:.4}.tabs-striped.tabs-color-positive .tab-item.activated,.tabs-striped.tabs-color-positive .tab-item.active,.tabs-striped.tabs-color-positive .tab-item.tab-item-active{margin-top:-2px;color:#387ef5;border:0 solid #387ef5;border-top-width:2px}.tabs-striped.tabs-color-positive .tab-item.activated .badge,.tabs-striped.tabs-color-positive .tab-item.active .badge,.tabs-striped.tabs-color-positive .tab-item.tab-item-active .badge{top:2px;opacity:1}.tabs-striped.tabs-color-calm .tab-item{color:rgba(17,193,243,.4);opacity:1}.tabs-striped.tabs-color-calm .tab-item .badge{opacity:.4}.tabs-striped.tabs-color-calm .tab-item.activated,.tabs-striped.tabs-color-calm .tab-item.active,.tabs-striped.tabs-color-calm .tab-item.tab-item-active{margin-top:-2px;color:#11c1f3;border:0 solid #11c1f3;border-top-width:2px}.tabs-striped.tabs-color-calm .tab-item.activated .badge,.tabs-striped.tabs-color-calm .tab-item.active .badge,.tabs-striped.tabs-color-calm .tab-item.tab-item-active .badge{top:2px;opacity:1}.tabs-striped.tabs-color-assertive .tab-item{color:rgba(239,71,58,.4);opacity:1}.tabs-striped.tabs-color-assertive .tab-item .badge{opacity:.4}.tabs-striped.tabs-color-assertive .tab-item.activated,.tabs-striped.tabs-color-assertive .tab-item.active,.tabs-striped.tabs-color-assertive .tab-item.tab-item-active{margin-top:-2px;color:#ef473a;border:0 solid #ef473a;border-top-width:2px}.tabs-striped.tabs-color-assertive .tab-item.activated .badge,.tabs-striped.tabs-color-assertive .tab-item.active .badge,.tabs-striped.tabs-color-assertive .tab-item.tab-item-active .badge{top:2px;opacity:1}.tabs-striped.tabs-color-balanced .tab-item{color:rgba(51,205,95,.4);opacity:1}.tabs-striped.tabs-color-balanced .tab-item .badge{opacity:.4}.tabs-striped.tabs-color-balanced .tab-item.activated,.tabs-striped.tabs-color-balanced .tab-item.active,.tabs-striped.tabs-color-balanced .tab-item.tab-item-active{margin-top:-2px;color:#33cd5f;border:0 solid #33cd5f;border-top-width:2px}.tabs-striped.tabs-color-balanced .tab-item.activated .badge,.tabs-striped.tabs-color-balanced .tab-item.active .badge,.tabs-striped.tabs-color-balanced .tab-item.tab-item-active .badge{top:2px;opacity:1}.tabs-striped.tabs-color-energized .tab-item{color:rgba(255,201,0,.4);opacity:1}.tabs-striped.tabs-color-energized .tab-item .badge{opacity:.4}.tabs-striped.tabs-color-energized .tab-item.activated,.tabs-striped.tabs-color-energized .tab-item.active,.tabs-striped.tabs-color-energized .tab-item.tab-item-active{margin-top:-2px;color:#ffc900;border:0 solid #ffc900;border-top-width:2px}.tabs-striped.tabs-color-energized .tab-item.activated .badge,.tabs-striped.tabs-color-energized .tab-item.active .badge,.tabs-striped.tabs-color-energized .tab-item.tab-item-active .badge{top:2px;opacity:1}.tabs-striped.tabs-color-royal .tab-item{color:rgba(136,106,234,.4);opacity:1}.tabs-striped.tabs-color-royal .tab-item .badge{opacity:.4}.tabs-striped.tabs-color-royal .tab-item.activated,.tabs-striped.tabs-color-royal .tab-item.active,.tabs-striped.tabs-color-royal .tab-item.tab-item-active{margin-top:-2px;color:#886aea;border:0 solid #886aea;border-top-width:2px}.tabs-striped.tabs-color-royal .tab-item.activated .badge,.tabs-striped.tabs-color-royal .tab-item.active .badge,.tabs-striped.tabs-color-royal .tab-item.tab-item-active .badge{top:2px;opacity:1}.tabs-striped.tabs-color-dark .tab-item{color:rgba(68,68,68,.4);opacity:1}.tabs-striped.tabs-color-dark .tab-item .badge{opacity:.4}.tabs-striped.tabs-color-dark .tab-item.activated,.tabs-striped.tabs-color-dark .tab-item.active,.tabs-striped.tabs-color-dark .tab-item.tab-item-active{margin-top:-2px;color:#444;border:0 solid #444;border-top-width:2px}.tabs-striped.tabs-color-dark .tab-item.activated .badge,.tabs-striped.tabs-color-dark .tab-item.active .badge,.tabs-striped.tabs-color-dark .tab-item.tab-item-active .badge{top:2px;opacity:1}.tabs-background-light .tabs,.tabs-background-light>.tabs{background-color:#fff;background-image:linear-gradient(0deg,#ddd,#ddd 50%,transparent 50%);border-color:#ddd}.tabs-background-stable .tabs,.tabs-background-stable>.tabs{background-color:#f8f8f8;background-image:linear-gradient(0deg,#b2b2b2,#b2b2b2 50%,transparent 50%);border-color:#b2b2b2}.tabs-background-positive .tabs,.tabs-background-positive>.tabs{background-color:#387ef5;background-image:linear-gradient(0deg,#0c60ee,#0c60ee 50%,transparent 50%);border-color:#0c60ee}.tabs-background-calm .tabs,.tabs-background-calm>.tabs{background-color:#11c1f3;background-image:linear-gradient(0deg,#0a9dc7,#0a9dc7 50%,transparent 50%);border-color:#0a9dc7}.tabs-background-assertive .tabs,.tabs-background-assertive>.tabs{background-color:#ef473a;background-image:linear-gradient(0deg,#e42112,#e42112 50%,transparent 50%);border-color:#e42112}.tabs-background-balanced .tabs,.tabs-background-balanced>.tabs{background-color:#33cd5f;background-image:linear-gradient(0deg,#28a54c,#28a54c 50%,transparent 50%);border-color:#28a54c}.tabs-background-energized .tabs,.tabs-background-energized>.tabs{background-color:#ffc900;background-image:linear-gradient(0deg,#e6b500,#e6b500 50%,transparent 50%);border-color:#e6b500}.tabs-background-royal .tabs,.tabs-background-royal>.tabs{background-color:#886aea;background-image:linear-gradient(0deg,#6b46e5,#6b46e5 50%,transparent 50%);border-color:#6b46e5}.tabs-background-dark .tabs,.tabs-background-dark>.tabs{background-color:#444;background-image:linear-gradient(0deg,#111,#111 50%,transparent 50%);border-color:#111}.tabs-color-light .tab-item{color:rgba(255,255,255,.4);opacity:1}.tabs-color-light .tab-item .badge{opacity:.4}.tabs-color-light .tab-item.activated,.tabs-color-light .tab-item.active,.tabs-color-light .tab-item.tab-item-active{color:#fff;border:0 solid #fff}.tabs-color-light .tab-item.activated .badge,.tabs-color-light .tab-item.active .badge,.tabs-color-light .tab-item.tab-item-active .badge{opacity:1}.tabs-color-stable .tab-item{color:rgba(248,248,248,.4);opacity:1}.tabs-color-stable .tab-item .badge{opacity:.4}.tabs-color-stable .tab-item.activated,.tabs-color-stable .tab-item.active,.tabs-color-stable .tab-item.tab-item-active{color:#f8f8f8;border:0 solid #f8f8f8}.tabs-color-stable .tab-item.activated .badge,.tabs-color-stable .tab-item.active .badge,.tabs-color-stable .tab-item.tab-item-active .badge{opacity:1}.tabs-color-positive .tab-item{color:rgba(56,126,245,.4);opacity:1}.tabs-color-positive .tab-item .badge{opacity:.4}.tabs-color-positive .tab-item.activated,.tabs-color-positive .tab-item.active,.tabs-color-positive .tab-item.tab-item-active{color:#387ef5;border:0 solid #387ef5}.tabs-color-positive .tab-item.activated .badge,.tabs-color-positive .tab-item.active .badge,.tabs-color-positive .tab-item.tab-item-active .badge{opacity:1}.tabs-color-calm .tab-item{color:rgba(17,193,243,.4);opacity:1}.tabs-color-calm .tab-item .badge{opacity:.4}.tabs-color-calm .tab-item.activated,.tabs-color-calm .tab-item.active,.tabs-color-calm .tab-item.tab-item-active{color:#11c1f3;border:0 solid #11c1f3}.tabs-color-calm .tab-item.activated .badge,.tabs-color-calm .tab-item.active .badge,.tabs-color-calm .tab-item.tab-item-active .badge{opacity:1}.tabs-color-assertive .tab-item{color:rgba(239,71,58,.4);opacity:1}.tabs-color-assertive .tab-item .badge{opacity:.4}.tabs-color-assertive .tab-item.activated,.tabs-color-assertive .tab-item.active,.tabs-color-assertive .tab-item.tab-item-active{color:#ef473a;border:0 solid #ef473a}.tabs-color-assertive .tab-item.activated .badge,.tabs-color-assertive .tab-item.active .badge,.tabs-color-assertive .tab-item.tab-item-active .badge{opacity:1}.tabs-color-balanced .tab-item{color:rgba(51,205,95,.4);opacity:1}.tabs-color-balanced .tab-item .badge{opacity:.4}.tabs-color-balanced .tab-item.activated,.tabs-color-balanced .tab-item.active,.tabs-color-balanced .tab-item.tab-item-active{color:#33cd5f;border:0 solid #33cd5f}.tabs-color-balanced .tab-item.activated .badge,.tabs-color-balanced .tab-item.active .badge,.tabs-color-balanced .tab-item.tab-item-active .badge{opacity:1}.tabs-color-energized .tab-item{color:rgba(255,201,0,.4);opacity:1}.tabs-color-energized .tab-item .badge{opacity:.4}.tabs-color-energized .tab-item.activated,.tabs-color-energized .tab-item.active,.tabs-color-energized .tab-item.tab-item-active{color:#ffc900;border:0 solid #ffc900}.tabs-color-energized .tab-item.activated .badge,.tabs-color-energized .tab-item.active .badge,.tabs-color-energized .tab-item.tab-item-active .badge{opacity:1}.tabs-color-royal .tab-item{color:rgba(136,106,234,.4);opacity:1}.tabs-color-royal .tab-item .badge{opacity:.4}.tabs-color-royal .tab-item.activated,.tabs-color-royal .tab-item.active,.tabs-color-royal .tab-item.tab-item-active{color:#886aea;border:0 solid #886aea}.tabs-color-royal .tab-item.activated .badge,.tabs-color-royal .tab-item.active .badge,.tabs-color-royal .tab-item.tab-item-active .badge{opacity:1}.tabs-color-dark .tab-item{color:rgba(68,68,68,.4);opacity:1}.tabs-color-dark .tab-item .badge{opacity:.4}.tabs-color-dark .tab-item.activated,.tabs-color-dark .tab-item.active,.tabs-color-dark .tab-item.tab-item-active{color:#444;border:0 solid #444}.tabs-color-dark .tab-item.activated .badge,.tabs-color-dark .tab-item.active .badge,.tabs-color-dark .tab-item.tab-item-active .badge{opacity:1}ion-tabs.tabs-color-active-light .tab-item{color:#444}ion-tabs.tabs-color-active-light .tab-item.activated,ion-tabs.tabs-color-active-light .tab-item.active,ion-tabs.tabs-color-active-light .tab-item.tab-item-active{color:#fff}ion-tabs.tabs-striped.tabs-color-active-light .tab-item.activated,ion-tabs.tabs-striped.tabs-color-active-light .tab-item.active,ion-tabs.tabs-striped.tabs-color-active-light .tab-item.tab-item-active{border-color:#fff;color:#fff}ion-tabs.tabs-color-active-stable .tab-item{color:#444}ion-tabs.tabs-color-active-stable .tab-item.activated,ion-tabs.tabs-color-active-stable .tab-item.active,ion-tabs.tabs-color-active-stable .tab-item.tab-item-active{color:#f8f8f8}ion-tabs.tabs-striped.tabs-color-active-stable .tab-item.activated,ion-tabs.tabs-striped.tabs-color-active-stable .tab-item.active,ion-tabs.tabs-striped.tabs-color-active-stable .tab-item.tab-item-active{border-color:#f8f8f8;color:#f8f8f8}ion-tabs.tabs-color-active-positive .tab-item{color:#444}ion-tabs.tabs-color-active-positive .tab-item.activated,ion-tabs.tabs-color-active-positive .tab-item.active,ion-tabs.tabs-color-active-positive .tab-item.tab-item-active{color:#387ef5}ion-tabs.tabs-striped.tabs-color-active-positive .tab-item.activated,ion-tabs.tabs-striped.tabs-color-active-positive .tab-item.active,ion-tabs.tabs-striped.tabs-color-active-positive .tab-item.tab-item-active{border-color:#387ef5;color:#387ef5}ion-tabs.tabs-color-active-calm .tab-item{color:#444}ion-tabs.tabs-color-active-calm .tab-item.activated,ion-tabs.tabs-color-active-calm .tab-item.active,ion-tabs.tabs-color-active-calm .tab-item.tab-item-active{color:#11c1f3}ion-tabs.tabs-striped.tabs-color-active-calm .tab-item.activated,ion-tabs.tabs-striped.tabs-color-active-calm .tab-item.active,ion-tabs.tabs-striped.tabs-color-active-calm .tab-item.tab-item-active{border-color:#11c1f3;color:#11c1f3}ion-tabs.tabs-color-active-assertive .tab-item{color:#444}ion-tabs.tabs-color-active-assertive .tab-item.activated,ion-tabs.tabs-color-active-assertive .tab-item.active,ion-tabs.tabs-color-active-assertive .tab-item.tab-item-active{color:#ef473a}ion-tabs.tabs-striped.tabs-color-active-assertive .tab-item.activated,ion-tabs.tabs-striped.tabs-color-active-assertive .tab-item.active,ion-tabs.tabs-striped.tabs-color-active-assertive .tab-item.tab-item-active{border-color:#ef473a;color:#ef473a}ion-tabs.tabs-color-active-balanced .tab-item{color:#444}ion-tabs.tabs-color-active-balanced .tab-item.activated,ion-tabs.tabs-color-active-balanced .tab-item.active,ion-tabs.tabs-color-active-balanced .tab-item.tab-item-active{color:#33cd5f}ion-tabs.tabs-striped.tabs-color-active-balanced .tab-item.activated,ion-tabs.tabs-striped.tabs-color-active-balanced .tab-item.active,ion-tabs.tabs-striped.tabs-color-active-balanced .tab-item.tab-item-active{border-color:#33cd5f;color:#33cd5f}ion-tabs.tabs-color-active-energized .tab-item{color:#444}ion-tabs.tabs-color-active-energized .tab-item.activated,ion-tabs.tabs-color-active-energized .tab-item.active,ion-tabs.tabs-color-active-energized .tab-item.tab-item-active{color:#ffc900}ion-tabs.tabs-striped.tabs-color-active-energized .tab-item.activated,ion-tabs.tabs-striped.tabs-color-active-energized .tab-item.active,ion-tabs.tabs-striped.tabs-color-active-energized .tab-item.tab-item-active{border-color:#ffc900;color:#ffc900}ion-tabs.tabs-color-active-royal .tab-item{color:#444}ion-tabs.tabs-color-active-royal .tab-item.activated,ion-tabs.tabs-color-active-royal .tab-item.active,ion-tabs.tabs-color-active-royal .tab-item.tab-item-active{color:#886aea}ion-tabs.tabs-striped.tabs-color-active-royal .tab-item.activated,ion-tabs.tabs-striped.tabs-color-active-royal .tab-item.active,ion-tabs.tabs-striped.tabs-color-active-royal .tab-item.tab-item-active{border-color:#886aea;color:#886aea}ion-tabs.tabs-color-active-dark .tab-item{color:#fff}ion-tabs.tabs-color-active-dark .tab-item.activated,ion-tabs.tabs-color-active-dark .tab-item.active,ion-tabs.tabs-color-active-dark .tab-item.tab-item-active{color:#444}ion-tabs.tabs-striped.tabs-color-active-dark .tab-item.activated,ion-tabs.tabs-striped.tabs-color-active-dark .tab-item.active,ion-tabs.tabs-striped.tabs-color-active-dark .tab-item.tab-item-active{border-color:#444;color:#444}.tabs-top.tabs-striped{padding-bottom:0}.tabs-top.tabs-striped .tab-item{background:0 0;-webkit-transition:color .1s ease;-moz-transition:color .1s ease;-ms-transition:color .1s ease;-o-transition:color .1s ease;transition:color .1s ease}.tabs-top.tabs-striped .tab-item.activated,.tabs-top.tabs-striped .tab-item.active,.tabs-top.tabs-striped .tab-item.tab-item-active{margin-top:1px;border-width:0 0 2px 0!important;border-style:solid}.tabs-top.tabs-striped .tab-item.activated>.badge,.tabs-top.tabs-striped .tab-item.activated>i,.tabs-top.tabs-striped .tab-item.active>.badge,.tabs-top.tabs-striped .tab-item.active>i,.tabs-top.tabs-striped .tab-item.tab-item-active>.badge,.tabs-top.tabs-striped .tab-item.tab-item-active>i{margin-top:-1px}.tabs-top.tabs-striped .tab-item .badge{-webkit-transition:color .2s ease;-moz-transition:color .2s ease;-ms-transition:color .2s ease;-o-transition:color .2s ease;transition:color .2s ease}.tabs-top.tabs-striped:not(.tabs-icon-left):not(.tabs-icon-top) .tab-item.activated .tab-title,.tabs-top.tabs-striped:not(.tabs-icon-left):not(.tabs-icon-top) .tab-item.activated i,.tabs-top.tabs-striped:not(.tabs-icon-left):not(.tabs-icon-top) .tab-item.active .tab-title,.tabs-top.tabs-striped:not(.tabs-icon-left):not(.tabs-icon-top) .tab-item.active i,.tabs-top.tabs-striped:not(.tabs-icon-left):not(.tabs-icon-top) .tab-item.tab-item-active .tab-title,.tabs-top.tabs-striped:not(.tabs-icon-left):not(.tabs-icon-top) .tab-item.tab-item-active i{display:block;margin-top:-1px}.tabs-top.tabs-striped.tabs-icon-left .tab-item{margin-top:1px}.tabs-top.tabs-striped.tabs-icon-left .tab-item.activated .tab-title,.tabs-top.tabs-striped.tabs-icon-left .tab-item.activated i,.tabs-top.tabs-striped.tabs-icon-left .tab-item.active .tab-title,.tabs-top.tabs-striped.tabs-icon-left .tab-item.active i,.tabs-top.tabs-striped.tabs-icon-left .tab-item.tab-item-active .tab-title,.tabs-top.tabs-striped.tabs-icon-left .tab-item.tab-item-active i{margin-top:-.1em}.tabs-top>.tabs,.tabs.tabs-top{top:44px;padding-top:0;background-position:bottom;border-top-width:0;border-bottom-width:1px}.tabs-top>.tabs .tab-item.activated .badge,.tabs-top>.tabs .tab-item.active .badge,.tabs-top>.tabs .tab-item.tab-item-active .badge,.tabs.tabs-top .tab-item.activated .badge,.tabs.tabs-top .tab-item.active .badge,.tabs.tabs-top .tab-item.tab-item-active .badge{top:4%}.tabs-top~.bar-header{border-bottom-width:0}.tab-item{-webkit-box-flex:1;-webkit-flex:1;-moz-box-flex:1;-moz-flex:1;-ms-flex:1;flex:1;display:block;overflow:hidden;max-width:150px;height:100%;color:inherit;text-align:center;text-decoration:none;text-overflow:ellipsis;white-space:nowrap;font-weight:400;font-size:14px;font-family:"-apple-system","Helvetica Neue",Roboto,"Segoe UI",sans-serif;opacity:.7}.tab-item:hover{cursor:pointer}.tab-item.tab-hidden,.tabs-item-hide>.tabs,.tabs.tabs-item-hide{display:none}.tabs-icon-bottom.tabs .tab-item,.tabs-icon-bottom>.tabs .tab-item,.tabs-icon-top.tabs .tab-item,.tabs-icon-top>.tabs .tab-item{font-size:10px;line-height:14px}.tab-item .icon{display:block;margin:0 auto;height:32px;font-size:32px}.tabs-icon-left.tabs .tab-item,.tabs-icon-left>.tabs .tab-item,.tabs-icon-right.tabs .tab-item,.tabs-icon-right>.tabs .tab-item{font-size:10px}.tabs-icon-left.tabs .tab-item .icon,.tabs-icon-left.tabs .tab-item .tab-title,.tabs-icon-left>.tabs .tab-item .icon,.tabs-icon-left>.tabs .tab-item .tab-title,.tabs-icon-right.tabs .tab-item .icon,.tabs-icon-right.tabs .tab-item .tab-title,.tabs-icon-right>.tabs .tab-item .icon,.tabs-icon-right>.tabs .tab-item .tab-title{display:inline-block;vertical-align:top;margin-top:-.1em}.tabs-icon-left.tabs .tab-item .icon:before,.tabs-icon-left.tabs .tab-item .tab-title:before,.tabs-icon-left>.tabs .tab-item .icon:before,.tabs-icon-left>.tabs .tab-item .tab-title:before,.tabs-icon-right.tabs .tab-item .icon:before,.tabs-icon-right.tabs .tab-item .tab-title:before,.tabs-icon-right>.tabs .tab-item .icon:before,.tabs-icon-right>.tabs .tab-item .tab-title:before{font-size:24px;line-height:49px}.tabs-icon-left.tabs .tab-item .icon,.tabs-icon-left>.tabs .tab-item .icon{padding-right:3px}.tabs-icon-right.tabs .tab-item .icon,.tabs-icon-right>.tabs .tab-item .icon{padding-left:3px}.tabs-icon-only.tabs .icon,.tabs-icon-only>.tabs .icon{line-height:inherit}.tab-item.has-badge{position:relative}.tab-item .badge{position:absolute;top:4%;right:33%;right:calc(50% - 26px);padding:1px 6px;height:auto;font-size:12px;line-height:16px}.tab-item.activated,.tab-item.active,.tab-item.tab-item-active{opacity:1}.tab-item.activated.tab-item-light,.tab-item.active.tab-item-light,.tab-item.tab-item-active.tab-item-light{color:#fff}.tab-item.activated.tab-item-stable,.tab-item.active.tab-item-stable,.tab-item.tab-item-active.tab-item-stable{color:#f8f8f8}.tab-item.activated.tab-item-positive,.tab-item.active.tab-item-positive,.tab-item.tab-item-active.tab-item-positive{color:#387ef5}.tab-item.activated.tab-item-calm,.tab-item.active.tab-item-calm,.tab-item.tab-item-active.tab-item-calm{color:#11c1f3}.tab-item.activated.tab-item-assertive,.tab-item.active.tab-item-assertive,.tab-item.tab-item-active.tab-item-assertive{color:#ef473a}.tab-item.activated.tab-item-balanced,.tab-item.active.tab-item-balanced,.tab-item.tab-item-active.tab-item-balanced{color:#33cd5f}.tab-item.activated.tab-item-energized,.tab-item.active.tab-item-energized,.tab-item.tab-item-active.tab-item-energized{color:#ffc900}.tab-item.activated.tab-item-royal,.tab-item.active.tab-item-royal,.tab-item.tab-item-active.tab-item-royal{color:#886aea}.tab-item.activated.tab-item-dark,.tab-item.active.tab-item-dark,.tab-item.tab-item-active.tab-item-dark{color:#444}.item.tabs{display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-moz-flex;display:-ms-flexbox;display:flex;padding:0}.item.tabs .icon:before{position:relative}.tab-item.disabled,.tab-item[disabled]{opacity:.4;cursor:default;pointer-events:none}.nav-bar-tabs-top.hide~.view-container .tabs-top .tabs{top:0}.pane[hide-nav-bar=true] .has-tabs-top{top:49px}.menu{position:absolute;top:0;bottom:0;z-index:0;overflow:hidden;min-height:100%;max-height:100%;width:275px;background-color:#fff}.menu .scroll-content{z-index:10}.menu .bar-header{z-index:11}.menu-content{-webkit-transform:none;transform:none;box-shadow:-1px 0 2px rgba(0,0,0,.2),1px 0 2px rgba(0,0,0,.2)}.menu-open .menu-content .pane,.menu-open .menu-content .scroll-content,.menu-open .menu-content .scroll-content .scroll{pointer-events:none}.menu-open .menu-content .scroll-content:not(.overflow-scroll){overflow:hidden}.grade-b .menu-content,.grade-c .menu-content{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;right:-1px;left:-1px;border-right:1px solid #ccc;border-left:1px solid #ccc;box-shadow:none}.menu-left{left:0}.menu-right{right:0}.aside-open.aside-resizing .menu-right{display:none}.menu-animated{-webkit-transition:-webkit-transform 200ms ease;transition:transform 200ms ease}.modal-backdrop,.modal-backdrop-bg{position:fixed;top:0;left:0;z-index:10;width:100%;height:100%}.modal-backdrop-bg{pointer-events:none}.modal{display:block;position:absolute;top:0;z-index:10;overflow:hidden;min-height:100%;width:100%;background-color:#fff}@media (min-width:680px){.modal{top:20%;right:20%;bottom:20%;left:20%;min-height:240px;width:60%}.modal.ng-leave-active{bottom:0}.platform-ios.platform-cordova .modal-wrapper .modal .bar-header:not(.bar-subheader){height:44px}.platform-ios.platform-cordova .modal-wrapper .modal .bar-header:not(.bar-subheader)>*{margin-top:0}.platform-ios.platform-cordova .modal-wrapper .modal .bar-subheader,.platform-ios.platform-cordova .modal-wrapper .modal .has-header,.platform-ios.platform-cordova .modal-wrapper .modal .tabs-top>.tabs,.platform-ios.platform-cordova .modal-wrapper .modal .tabs.tabs-top{top:44px}.platform-ios.platform-cordova .modal-wrapper .modal .has-subheader{top:88px}.platform-ios.platform-cordova .modal-wrapper .modal .has-header.has-tabs-top{top:93px}.platform-ios.platform-cordova .modal-wrapper .modal .has-header.has-subheader.has-tabs-top{top:137px}.modal-backdrop-bg{-webkit-transition:opacity 300ms ease-in-out;transition:opacity 300ms ease-in-out;background-color:#000;opacity:0}.active .modal-backdrop-bg{opacity:.5}}.modal-open{pointer-events:none}.modal-open .modal,.modal-open .modal-backdrop{pointer-events:auto}.modal-open.loading-active .modal,.modal-open.loading-active .modal-backdrop{pointer-events:none}.popover-backdrop{position:fixed;top:0;left:0;z-index:10;width:100%;height:100%;background-color:transparent}.popover-backdrop.active{background-color:rgba(0,0,0,.1)}.popover{position:absolute;top:25%;left:50%;z-index:10;display:block;margin-top:12px;margin-left:-110px;height:280px;width:220px;background-color:#fff;box-shadow:0 1px 3px rgba(0,0,0,.4);opacity:0}.popover .item:first-child{border-top:0}.popover .item:last-child{border-bottom:0}.popover.popover-bottom{margin-top:-12px}.popover,.popover .bar-header{border-radius:2px}.popover .scroll-content{z-index:1;margin:2px 0}.popover .bar-header{border-bottom-right-radius:0;border-bottom-left-radius:0}.popover .has-header{border-top-right-radius:0;border-top-left-radius:0}.popover-arrow{display:none}.platform-ios .popover{box-shadow:0 0 40px rgba(0,0,0,.08);border-radius:10px}.platform-ios .popover .bar-header{-webkit-border-top-right-radius:10px;border-top-right-radius:10px;-webkit-border-top-left-radius:10px;border-top-left-radius:10px}.platform-ios .popover .scroll-content{margin:8px 0;border-radius:10px}.platform-ios .popover .scroll-content.has-header{margin-top:0}.platform-ios .popover-arrow{position:absolute;display:block;top:-17px;width:30px;height:19px;overflow:hidden}.platform-ios .popover-arrow:after{position:absolute;top:12px;left:5px;width:20px;height:20px;background-color:#fff;border-radius:3px;content:'';-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.platform-ios .popover-bottom .popover-arrow{top:auto;bottom:-10px}.platform-ios .popover-bottom .popover-arrow:after{top:-6px}.platform-android .popover{margin-top:-32px;background-color:#fafafa;box-shadow:0 2px 6px rgba(0,0,0,.35)}.platform-android .popover .item{border-color:#fafafa;background-color:#fafafa;color:#4d4d4d}.platform-android .popover.popover-bottom{margin-top:32px}.platform-android .popover-backdrop,.platform-android .popover-backdrop.active{background-color:transparent}.popover-open{pointer-events:none}.popover-open .popover,.popover-open .popover-backdrop{pointer-events:auto}.popover-open.loading-active .popover,.popover-open.loading-active .popover-backdrop{pointer-events:none}@media (min-width:680px){.popover{width:360px;margin-left:-180px}}.popup-container{position:absolute;top:0;left:0;bottom:0;right:0;background:0 0;display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-moz-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;-moz-justify-content:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;-webkit-align-items:center;-moz-align-items:center;align-items:center;z-index:12;visibility:hidden}.popup-container.popup-showing{visibility:visible}.popup-container.popup-hidden .popup{-webkit-animation-name:scaleOut;animation-name:scaleOut;-webkit-animation-duration:.1s;animation-duration:.1s;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-animation-fill-mode:both;animation-fill-mode:both}.popup-container.active .popup{-webkit-animation-name:superScaleIn;animation-name:superScaleIn;-webkit-animation-duration:.2s;animation-duration:.2s;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-animation-fill-mode:both;animation-fill-mode:both}.popup-container .popup{width:250px;max-width:100%;max-height:90%;border-radius:0;background-color:rgba(255,255,255,.9);display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-moz-flex;display:-ms-flexbox;display:flex;-webkit-box-direction:normal;-webkit-box-orient:vertical;-webkit-flex-direction:column;-moz-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.popup-container input,.popup-container textarea{width:100%}.popup-head{padding:15px 10px;border-bottom:1px solid #eee;text-align:center}.popup-title{margin:0;padding:0;font-size:15px}.popup-sub-title{margin:5px 0 0 0;padding:0;font-weight:400;font-size:11px}.popup-body{padding:10px;overflow:auto}.popup-buttons{display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-moz-flex;display:-ms-flexbox;display:flex;-webkit-box-direction:normal;-webkit-box-orient:horizontal;-webkit-flex-direction:row;-moz-flex-direction:row;-ms-flex-direction:row;flex-direction:row;padding:10px;min-height:65px}.popup-buttons .button{-webkit-box-flex:1;-webkit-flex:1;-moz-box-flex:1;-moz-flex:1;-ms-flex:1;flex:1;display:block;min-height:45px;border-radius:2px;line-height:20px;margin-right:5px}.popup-buttons .button:last-child{margin-right:0}.popup-open,.popup-open.modal-open .modal{pointer-events:none}.popup-open .popup,.popup-open .popup-backdrop{pointer-events:auto}.loading-container{position:absolute;left:0;top:0;right:0;bottom:0;z-index:13;display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-moz-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;-moz-justify-content:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;-webkit-align-items:center;-moz-align-items:center;align-items:center;-webkit-transition:.2s opacity linear;transition:.2s opacity linear;visibility:hidden;opacity:0}.loading-container:not(.visible) .icon,.loading-container:not(.visible) .spinner{display:none}.loading-container.visible{visibility:visible}.loading-container.active{opacity:1}.loading-container .loading{padding:20px;border-radius:5px;background-color:rgba(0,0,0,.7);color:#fff;text-align:center;text-overflow:ellipsis;font-size:15px}.loading-container .loading h1,.loading-container .loading h2,.loading-container .loading h3,.loading-container .loading h4,.loading-container .loading h5,.loading-container .loading h6{color:#fff}.item{border-color:#ddd;background-color:#fff;color:#444;position:relative;z-index:2;display:block;margin:-1px;padding:16px;border-width:1px;border-style:solid;font-size:16px}.item h2{margin:0 0 2px 0;font-size:16px;font-weight:400}.item h3{margin:0 0 4px 0;font-size:14px}.item h4{margin:0 0 4px 0;font-size:12px}.item h5,.item h6{margin:0 0 3px 0;font-size:10px}.item p{color:#666;font-size:14px;margin-bottom:2px}.item h1:last-child,.item h2:last-child,.item h3:last-child,.item h4:last-child,.item h5:last-child,.item h6:last-child,.item p:last-child{margin-bottom:0}.item .badge{display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-moz-flex;display:-ms-flexbox;display:flex;position:absolute;top:16px;right:32px}.item.item-button-right .badge{right:67px}.item.item-divider .badge{top:8px}.item .badge+.badge{margin-right:5px}.item.item-light{border-color:#ddd;background-color:#fff;color:#444}.item.item-stable{border-color:#b2b2b2;background-color:#f8f8f8;color:#444}.item.item-positive{border-color:#0c60ee;background-color:#387ef5;color:#fff}.item.item-calm{border-color:#0a9dc7;background-color:#11c1f3;color:#fff}.item.item-assertive{border-color:#e42112;background-color:#ef473a;color:#fff}.item.item-balanced{border-color:#28a54c;background-color:#33cd5f;color:#fff}.item.item-energized{border-color:#e6b500;background-color:#ffc900;color:#fff}.item.item-royal{border-color:#6b46e5;background-color:#886aea;color:#fff}.item.item-dark{border-color:#111;background-color:#444;color:#fff}.item[ng-click]:hover{cursor:pointer}.item-borderless,.list-borderless .item{border-width:0}.item .item-content.activated,.item .item-content.activated.item-complex>.item-content,.item .item-content.active,.item .item-content.active.item-complex>.item-content,.item-complex.activated .item-content,.item-complex.activated .item-content.item-complex>.item-content,.item-complex.active .item-content,.item-complex.active .item-content.item-complex>.item-content,.item.activated,.item.activated.item-complex>.item-content,.item.active,.item.active.item-complex>.item-content{border-color:#ccc;background-color:#D9D9D9}.item .item-content.activated.item-light,.item .item-content.activated.item-light.item-complex>.item-content,.item .item-content.active.item-light,.item .item-content.active.item-light.item-complex>.item-content,.item-complex.activated .item-content.item-light,.item-complex.activated .item-content.item-light.item-complex>.item-content,.item-complex.active .item-content.item-light,.item-complex.active .item-content.item-light.item-complex>.item-content,.item.activated.item-light,.item.activated.item-light.item-complex>.item-content,.item.active.item-light,.item.active.item-light.item-complex>.item-content{border-color:#ccc;background-color:#fafafa}.item .item-content.activated.item-stable,.item .item-content.activated.item-stable.item-complex>.item-content,.item .item-content.active.item-stable,.item .item-content.active.item-stable.item-complex>.item-content,.item-complex.activated .item-content.item-stable,.item-complex.activated .item-content.item-stable.item-complex>.item-content,.item-complex.active .item-content.item-stable,.item-complex.active .item-content.item-stable.item-complex>.item-content,.item.activated.item-stable,.item.activated.item-stable.item-complex>.item-content,.item.active.item-stable,.item.active.item-stable.item-complex>.item-content{border-color:#a2a2a2;background-color:#e5e5e5}.item .item-content.activated.item-positive,.item .item-content.activated.item-positive.item-complex>.item-content,.item .item-content.active.item-positive,.item .item-content.active.item-positive.item-complex>.item-content,.item-complex.activated .item-content.item-positive,.item-complex.activated .item-content.item-positive.item-complex>.item-content,.item-complex.active .item-content.item-positive,.item-complex.active .item-content.item-positive.item-complex>.item-content,.item.activated.item-positive,.item.activated.item-positive.item-complex>.item-content,.item.active.item-positive,.item.active.item-positive.item-complex>.item-content{border-color:#0c60ee;background-color:#0c60ee}.item .item-content.activated.item-calm,.item .item-content.activated.item-calm.item-complex>.item-content,.item .item-content.active.item-calm,.item .item-content.active.item-calm.item-complex>.item-content,.item-complex.activated .item-content.item-calm,.item-complex.activated .item-content.item-calm.item-complex>.item-content,.item-complex.active .item-content.item-calm,.item-complex.active .item-content.item-calm.item-complex>.item-content,.item.activated.item-calm,.item.activated.item-calm.item-complex>.item-content,.item.active.item-calm,.item.active.item-calm.item-complex>.item-content{border-color:#0a9dc7;background-color:#0a9dc7}.item .item-content.activated.item-assertive,.item .item-content.activated.item-assertive.item-complex>.item-content,.item .item-content.active.item-assertive,.item .item-content.active.item-assertive.item-complex>.item-content,.item-complex.activated .item-content.item-assertive,.item-complex.activated .item-content.item-assertive.item-complex>.item-content,.item-complex.active .item-content.item-assertive,.item-complex.active .item-content.item-assertive.item-complex>.item-content,.item.activated.item-assertive,.item.activated.item-assertive.item-complex>.item-content,.item.active.item-assertive,.item.active.item-assertive.item-complex>.item-content{border-color:#e42112;background-color:#e42112}.item .item-content.activated.item-balanced,.item .item-content.activated.item-balanced.item-complex>.item-content,.item .item-content.active.item-balanced,.item .item-content.active.item-balanced.item-complex>.item-content,.item-complex.activated .item-content.item-balanced,.item-complex.activated .item-content.item-balanced.item-complex>.item-content,.item-complex.active .item-content.item-balanced,.item-complex.active .item-content.item-balanced.item-complex>.item-content,.item.activated.item-balanced,.item.activated.item-balanced.item-complex>.item-content,.item.active.item-balanced,.item.active.item-balanced.item-complex>.item-content{border-color:#28a54c;background-color:#28a54c}.item .item-content.activated.item-energized,.item .item-content.activated.item-energized.item-complex>.item-content,.item .item-content.active.item-energized,.item .item-content.active.item-energized.item-complex>.item-content,.item-complex.activated .item-content.item-energized,.item-complex.activated .item-content.item-energized.item-complex>.item-content,.item-complex.active .item-content.item-energized,.item-complex.active .item-content.item-energized.item-complex>.item-content,.item.activated.item-energized,.item.activated.item-energized.item-complex>.item-content,.item.active.item-energized,.item.active.item-energized.item-complex>.item-content{border-color:#e6b500;background-color:#e6b500}.item .item-content.activated.item-royal,.item .item-content.activated.item-royal.item-complex>.item-content,.item .item-content.active.item-royal,.item .item-content.active.item-royal.item-complex>.item-content,.item-complex.activated .item-content.item-royal,.item-complex.activated .item-content.item-royal.item-complex>.item-content,.item-complex.active .item-content.item-royal,.item-complex.active .item-content.item-royal.item-complex>.item-content,.item.activated.item-royal,.item.activated.item-royal.item-complex>.item-content,.item.active.item-royal,.item.active.item-royal.item-complex>.item-content{border-color:#6b46e5;background-color:#6b46e5}.item .item-content.activated.item-dark,.item .item-content.activated.item-dark.item-complex>.item-content,.item .item-content.active.item-dark,.item .item-content.active.item-dark.item-complex>.item-content,.item-complex.activated .item-content.item-dark,.item-complex.activated .item-content.item-dark.item-complex>.item-content,.item-complex.active .item-content.item-dark,.item-complex.active .item-content.item-dark.item-complex>.item-content,.item.activated.item-dark,.item.activated.item-dark.item-complex>.item-content,.item.active.item-dark,.item.active.item-dark.item-complex>.item-content{border-color:#000;background-color:#262626}.item,.item h1,.item h2,.item h3,.item h4,.item h5,.item h6,.item p,.item-content,.item-content h1,.item-content h2,.item-content h3,.item-content h4,.item-content h5,.item-content h6,.item-content p{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}a.item{color:inherit;text-decoration:none}a.item:focus,a.item:hover{text-decoration:none}.item-complex,a.item.item-complex,button.item.item-complex{padding:0}.item-complex .item-content,.item-radio .item-content{position:relative;z-index:2;padding:16px 49px 16px 16px;border:none;background-color:#fff}a.item-content{display:block;color:inherit;text-decoration:none}.item-body h1,.item-body h2,.item-body h3,.item-body h4,.item-body h5,.item-body h6,.item-body p,.item-complex.item-text-wrap,.item-complex.item-text-wrap .item-content,.item-complex.item-text-wrap h1,.item-complex.item-text-wrap h2,.item-complex.item-text-wrap h3,.item-complex.item-text-wrap h4,.item-complex.item-text-wrap h5,.item-complex.item-text-wrap h6,.item-complex.item-text-wrap p,.item-text-wrap,.item-text-wrap .item,.item-text-wrap .item-content,.item-text-wrap h1,.item-text-wrap h2,.item-text-wrap h3,.item-text-wrap h4,.item-text-wrap h5,.item-text-wrap h6,.item-text-wrap p{overflow:visible;white-space:normal}.item-complex.item-light>.item-content{border-color:#ddd;background-color:#fff;color:#444}.item-complex.item-light>.item-content.active,.item-complex.item-light>.item-content.active.item-complex>.item-content,.item-complex.item-light>.item-content:active,.item-complex.item-light>.item-content:active.item-complex>.item-content{border-color:#ccc;background-color:#fafafa}.item-complex.item-stable>.item-content{border-color:#b2b2b2;background-color:#f8f8f8;color:#444}.item-complex.item-stable>.item-content.active,.item-complex.item-stable>.item-content.active.item-complex>.item-content,.item-complex.item-stable>.item-content:active,.item-complex.item-stable>.item-content:active.item-complex>.item-content{border-color:#a2a2a2;background-color:#e5e5e5}.item-complex.item-positive>.item-content{border-color:#0c60ee;background-color:#387ef5;color:#fff}.item-complex.item-positive>.item-content.active,.item-complex.item-positive>.item-content.active.item-complex>.item-content,.item-complex.item-positive>.item-content:active,.item-complex.item-positive>.item-content:active.item-complex>.item-content{border-color:#0c60ee;background-color:#0c60ee}.item-complex.item-calm>.item-content{border-color:#0a9dc7;background-color:#11c1f3;color:#fff}.item-complex.item-calm>.item-content.active,.item-complex.item-calm>.item-content.active.item-complex>.item-content,.item-complex.item-calm>.item-content:active,.item-complex.item-calm>.item-content:active.item-complex>.item-content{border-color:#0a9dc7;background-color:#0a9dc7}.item-complex.item-assertive>.item-content{border-color:#e42112;background-color:#ef473a;color:#fff}.item-complex.item-assertive>.item-content.active,.item-complex.item-assertive>.item-content.active.item-complex>.item-content,.item-complex.item-assertive>.item-content:active,.item-complex.item-assertive>.item-content:active.item-complex>.item-content{border-color:#e42112;background-color:#e42112}.item-complex.item-balanced>.item-content{border-color:#28a54c;background-color:#33cd5f;color:#fff}.item-complex.item-balanced>.item-content.active,.item-complex.item-balanced>.item-content.active.item-complex>.item-content,.item-complex.item-balanced>.item-content:active,.item-complex.item-balanced>.item-content:active.item-complex>.item-content{border-color:#28a54c;background-color:#28a54c}.item-complex.item-energized>.item-content{border-color:#e6b500;background-color:#ffc900;color:#fff}.item-complex.item-energized>.item-content.active,.item-complex.item-energized>.item-content.active.item-complex>.item-content,.item-complex.item-energized>.item-content:active,.item-complex.item-energized>.item-content:active.item-complex>.item-content{border-color:#e6b500;background-color:#e6b500}.item-complex.item-royal>.item-content{border-color:#6b46e5;background-color:#886aea;color:#fff}.item-complex.item-royal>.item-content.active,.item-complex.item-royal>.item-content.active.item-complex>.item-content,.item-complex.item-royal>.item-content:active,.item-complex.item-royal>.item-content:active.item-complex>.item-content{border-color:#6b46e5;background-color:#6b46e5}.item-complex.item-dark>.item-content{border-color:#111;background-color:#444;color:#fff}.item-complex.item-dark>.item-content.active,.item-complex.item-dark>.item-content.active.item-complex>.item-content,.item-complex.item-dark>.item-content:active,.item-complex.item-dark>.item-content:active.item-complex>.item-content{border-color:#000;background-color:#262626}.item-icon-left .icon,.item-icon-right .icon{display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-moz-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;-webkit-align-items:center;-moz-align-items:center;align-items:center;position:absolute;top:0;height:100%;font-size:32px}.item-icon-left .icon:before,.item-icon-right .icon:before{display:block;width:32px;text-align:center}.item .fill-icon{min-width:30px;min-height:30px;font-size:28px}.item-icon-left{padding-left:54px}.item-icon-left .icon{left:11px}.item-complex.item-icon-left{padding-left:0}.item-complex.item-icon-left .item-content{padding-left:54px}.item-icon-right{padding-right:54px}.item-icon-right .icon{right:11px}.item-complex.item-icon-right{padding-right:0}.item-complex.item-icon-right .item-content{padding-right:54px}.item-icon-left.item-icon-right .icon:first-child{right:auto}.item-icon-left .item-delete .icon,.item-icon-left.item-icon-right .icon:last-child{left:auto}.item-icon-left .icon-accessory,.item-icon-right .icon-accessory{color:#ccc;font-size:16px}.item-icon-left .icon-accessory{left:3px}.item-icon-right .icon-accessory{right:3px}.item-button-left{padding-left:72px}.item-button-left .item-content>.button,.item-button-left>.button{display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-moz-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;-webkit-align-items:center;-moz-align-items:center;align-items:center;position:absolute;top:8px;left:11px;min-width:34px;min-height:34px;font-size:18px;line-height:32px}.item-button-left .item-content>.button .icon:before,.item-button-left>.button .icon:before{position:relative;left:auto;width:auto;line-height:31px}.item-button-left .item-content>.button>.button,.item-button-left>.button>.button{margin:0 2px;min-height:34px;font-size:18px;line-height:32px}.item-button-right,a.item.item-button-right,button.item.item-button-right{padding-right:80px}.item-button-right .item-content>.button,.item-button-right .item-content>.buttons,.item-button-right>.button,.item-button-right>.buttons{display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-moz-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;-webkit-align-items:center;-moz-align-items:center;align-items:center;position:absolute;top:8px;right:16px;min-width:34px;min-height:34px;font-size:18px;line-height:32px}.item-button-right .item-content>.button .icon:before,.item-button-right .item-content>.buttons .icon:before,.item-button-right>.button .icon:before,.item-button-right>.buttons .icon:before{position:relative;left:auto;width:auto;line-height:31px}.item-button-right .item-content>.button>.button,.item-button-right .item-content>.buttons>.button,.item-button-right>.button>.button,.item-button-right>.buttons>.button{margin:0 2px;min-width:34px;min-height:34px;font-size:18px;line-height:32px}.item-button-left.item-button-right .button:first-child{right:auto}.item-button-left.item-button-right .button:last-child{left:auto}.item-avatar,.item-avatar .item-content,.item-avatar-left,.item-avatar-left .item-content{padding-left:72px;min-height:72px}.item-avatar .item-content .item-image,.item-avatar .item-content>img:first-child,.item-avatar .item-image,.item-avatar-left .item-content .item-image,.item-avatar-left .item-content>img:first-child,.item-avatar-left .item-image,.item-avatar-left>img:first-child,.item-avatar>img:first-child{position:absolute;top:16px;left:16px;max-width:40px;max-height:40px;width:100%;height:100%;border-radius:50%}.item-avatar-right,.item-avatar-right .item-content{padding-right:72px;min-height:72px}.item-avatar-right .item-content .item-image,.item-avatar-right .item-content>img:first-child,.item-avatar-right .item-image,.item-avatar-right>img:first-child{position:absolute;top:16px;right:16px;max-width:40px;max-height:40px;width:100%;height:100%;border-radius:50%}.item-thumbnail-left,.item-thumbnail-left .item-content{padding-top:8px;padding-left:106px;min-height:100px}.item-thumbnail-left .item-content .item-image,.item-thumbnail-left .item-content>img:first-child,.item-thumbnail-left .item-image,.item-thumbnail-left>img:first-child{position:absolute;top:10px;left:10px;max-width:80px;max-height:80px;width:100%;height:100%}.item-avatar-left.item-complex,.item-avatar.item-complex,.item-thumbnail-left.item-complex{padding-top:0;padding-left:0}.item-thumbnail-right,.item-thumbnail-right .item-content{padding-top:8px;padding-right:106px;min-height:100px}.item-thumbnail-right .item-content .item-image,.item-thumbnail-right .item-content>img:first-child,.item-thumbnail-right .item-image,.item-thumbnail-right>img:first-child{position:absolute;top:10px;right:10px;max-width:80px;max-height:80px;width:100%;height:100%}.item-avatar-right.item-complex,.item-thumbnail-right.item-complex{padding-top:0;padding-right:0}.item-image{padding:0;text-align:center}.item-image .list-img,.item-image img:first-child{width:100%;vertical-align:middle}.item-body{overflow:auto;padding:16px;text-overflow:inherit;white-space:normal}.item-body h1,.item-body h2,.item-body h3,.item-body h4,.item-body h5,.item-body h6,.item-body p{margin-top:16px;margin-bottom:16px}.item-divider{padding-top:8px;padding-bottom:8px;min-height:30px;background-color:#f5f5f5;color:#222;font-weight:500}.item-divider-ios,.platform-ios .item-divider-platform{padding-top:26px;text-transform:uppercase;font-weight:300;font-size:13px;background-color:#efeff4;color:#555}.item-divider-android,.platform-android .item-divider-platform{font-weight:300;font-size:13px}.item-note{float:right;color:#aaa;font-size:14px}.item-left-editable .item-content,.item-right-editable .item-content{-webkit-transition-duration:250ms;transition-duration:250ms;-webkit-transition-timing-function:ease-in-out;transition-timing-function:ease-in-out;-webkit-transition-property:-webkit-transform;-moz-transition-property:-moz-transform;transition-property:transform}.item-left-editing.item-left-editable .item-content,.list-left-editing .item-left-editable .item-content{-webkit-transform:translate3d(50px,0,0);transform:translate3d(50px,0,0)}.item-remove-animate.ng-leave{-webkit-transition-duration:300ms;transition-duration:300ms}.item-remove-animate.ng-leave .item-content,.item-remove-animate.ng-leave:last-of-type{-webkit-transition-duration:300ms;transition-duration:300ms;-webkit-transition-timing-function:ease-in;transition-timing-function:ease-in;-webkit-transition-property:all;transition-property:all}.item-remove-animate.ng-leave.ng-leave-active .item-content{opacity:0;-webkit-transform:translate3d(-100%,0,0)!important;transform:translate3d(-100%,0,0)!important}.item-remove-animate.ng-leave.ng-leave-active:last-of-type{opacity:0}.item-remove-animate.ng-leave.ng-leave-active~ion-item:not(.ng-leave){-webkit-transform:translate3d(0,-webkit-calc(-100% + 1px),0);transform:translate3d(0,calc(-100% + 1px),0);-webkit-transition-duration:300ms;transition-duration:300ms;-webkit-transition-timing-function:cubic-bezier(.25,.81,.24,1);transition-timing-function:cubic-bezier(.25,.81,.24,1);-webkit-transition-property:all;transition-property:all}.item-left-edit{-webkit-transition:all ease-in-out 125ms;transition:all ease-in-out 125ms;position:absolute;top:0;left:0;z-index:0;width:50px;height:100%;line-height:100%;display:none;opacity:0;-webkit-transform:translate3d(-21px,0,0);transform:translate3d(-21px,0,0)}.item-left-edit .button{height:100%}.item-left-edit .button.icon{display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-moz-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;-webkit-align-items:center;-moz-align-items:center;align-items:center;position:absolute;top:0;height:100%}.item-left-edit.visible{display:block}.item-left-edit.visible.active{opacity:1;-webkit-transform:translate3d(8px,0,0);transform:translate3d(8px,0,0)}.list-left-editing .item-left-edit{-webkit-transition-delay:125ms;transition-delay:125ms}.item-delete .button.icon{color:#ef473a;font-size:24px}.item-delete .button.icon:hover{opacity:.7}.item-right-edit{-webkit-transition:all ease-in-out 250ms;transition:all ease-in-out 250ms;position:absolute;top:0;right:0;z-index:3;width:75px;height:100%;background:inherit;padding-left:20px;display:block;opacity:0;-webkit-transform:translate3d(75px,0,0);transform:translate3d(75px,0,0)}.item-right-edit .button{min-width:50px;height:100%}.item-right-edit .button.icon{display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-moz-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;-webkit-align-items:center;-moz-align-items:center;align-items:center;position:absolute;top:0;height:100%;font-size:32px}.item-right-edit.visible{display:block}.item-right-edit.visible.active{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.item-reorder .button.icon{color:#444;font-size:32px}.item-reordering{position:absolute;left:0;top:0;z-index:9;width:100%;box-shadow:0 0 10px 0 #aaa}.item-reordering .item-reorder{z-index:9}.item-placeholder{opacity:.7}.item-options{position:absolute;top:0;right:0;z-index:1;height:100%}.item-options .button{height:100%;border:none;border-radius:0;display:-webkit-inline-box;display:-webkit-inline-flex;display:-moz-inline-flex;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;-webkit-align-items:center;-moz-align-items:center;align-items:center}.item-options .button:before{margin:0 auto}.list{position:relative;padding-top:1px;padding-bottom:1px;padding-left:0;margin-bottom:20px}.list:last-child{margin-bottom:0}.list:last-child.card{margin-bottom:40px}.list-header{margin-top:20px;padding:5px 15px;background-color:transparent;color:#222;font-weight:700}.card.list .list-item{padding-right:1px;padding-left:1px}.card,.list-inset{overflow:hidden;margin:20px 10px;border-radius:2px;background-color:#fff}.card{padding-top:1px;padding-bottom:1px;box-shadow:0 1px 3px rgba(0,0,0,.3)}.card .item{border-left:0;border-right:0}.card .item:first-child{border-top:0}.card .item:last-child{border-bottom:0}.padding .card,.padding .list-inset{margin-left:0;margin-right:0}.card .item:first-child,.card .item:first-child .item-content,.list-inset .item:first-child,.list-inset .item:first-child .item-content,.padding>.list .item:first-child,.padding>.list .item:first-child .item-content{border-top-left-radius:2px;border-top-right-radius:2px}.card .item:last-child,.card .item:last-child .item-content,.list-inset .item:last-child,.list-inset .item:last-child .item-content,.padding>.list .item:last-child,.padding>.list .item:last-child .item-content{border-bottom-right-radius:2px;border-bottom-left-radius:2px}.card .item:last-child,.list-inset .item:last-child{margin-bottom:-1px}.card .item,.list-inset .item,.padding-horizontal>.list .item,.padding>.list .item{margin-right:0;margin-left:0}.card .item.item-input input,.list-inset .item.item-input input,.padding-horizontal>.list .item.item-input input,.padding>.list .item.item-input input{padding-right:44px}.padding-left>.list .item{margin-left:0}.padding-right>.list .item{margin-right:0}.badge{background-color:transparent;color:#AAA;z-index:1;display:inline-block;padding:3px 8px;min-width:10px;border-radius:10px;vertical-align:baseline;text-align:center;white-space:nowrap;font-weight:700;font-size:14px;line-height:16px}.badge:empty{display:none}.badge.badge-light,.tabs .tab-item .badge.badge-light{background-color:#fff;color:#444}.badge.badge-stable,.tabs .tab-item .badge.badge-stable{background-color:#f8f8f8;color:#444}.badge.badge-positive,.tabs .tab-item .badge.badge-positive{background-color:#387ef5;color:#fff}.badge.badge-calm,.tabs .tab-item .badge.badge-calm{background-color:#11c1f3;color:#fff}.badge.badge-assertive,.tabs .tab-item .badge.badge-assertive{background-color:#ef473a;color:#fff}.badge.badge-balanced,.tabs .tab-item .badge.badge-balanced{background-color:#33cd5f;color:#fff}.badge.badge-energized,.tabs .tab-item .badge.badge-energized{background-color:#ffc900;color:#fff}.badge.badge-royal,.tabs .tab-item .badge.badge-royal{background-color:#886aea;color:#fff}.badge.badge-dark,.tabs .tab-item .badge.badge-dark{background-color:#444;color:#fff}.button .badge{position:relative;top:-1px}.slider{position:relative;visibility:hidden;overflow:hidden}.slider-slides{position:relative;height:100%}.slider-slide{position:relative;display:block;float:left;width:100%;height:100%;vertical-align:top}.slider-slide-image>img{width:100%}.slider-pager{position:absolute;bottom:20px;z-index:1;width:100%;height:15px;text-align:center}.slider-pager .slider-pager-page{display:inline-block;margin:0 3px;width:15px;color:#000;text-decoration:none;opacity:.3}.slider-pager .slider-pager-page.active{-webkit-transition:opacity .4s ease-in;transition:opacity .4s ease-in;opacity:1}.slider-pager-page.ng-animate,.slider-pager-page.ng-enter,.slider-pager-page.ng-leave,.slider-slide.ng-animate,.slider-slide.ng-enter,.slider-slide.ng-leave{-webkit-transition:none!important;transition:none!important}.slider-pager-page.ng-animate,.slider-slide.ng-animate{-webkit-animation:none 0s;animation:none 0s}.swiper-container{margin:0 auto;position:relative;z-index:1}.swiper-container-no-flexbox .swiper-slide{float:left}.swiper-container-vertical>.swiper-wrapper{-webkit-box-orient:vertical;-moz-box-orient:vertical;-ms-flex-direction:column;-webkit-flex-direction:column;flex-direction:column}.swiper-wrapper{z-index:1;display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex;-webkit-transition-property:-webkit-transform;-moz-transition-property:-moz-transform;-o-transition-property:-o-transform;-ms-transition-property:-ms-transform;transition-property:transform;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.swiper-container-android .swiper-slide,.swiper-wrapper{-webkit-transform:translate3d(0,0,0);-moz-transform:translate3d(0,0,0);-o-transform:translate(0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.swiper-container-multirow>.swiper-wrapper{-webkit-box-lines:multiple;-moz-box-lines:multiple;-ms-flex-wrap:wrap;-webkit-flex-wrap:wrap;flex-wrap:wrap}.swiper-container-free-mode>.swiper-wrapper{-webkit-transition-timing-function:ease-out;-moz-transition-timing-function:ease-out;-ms-transition-timing-function:ease-out;-o-transition-timing-function:ease-out;transition-timing-function:ease-out;margin:0 auto}.swiper-slide{display:block;-webkit-flex-shrink:0;-ms-flex:0 0 auto;flex-shrink:0;position:relative}.swiper-container-autoheight,.swiper-container-autoheight .swiper-slide{height:auto}.swiper-container-autoheight .swiper-wrapper{-webkit-box-align:start;-ms-flex-align:start;-webkit-align-items:flex-start;align-items:flex-start;-webkit-transition-property:-webkit-transform,height;-moz-transition-property:-moz-transform;-o-transition-property:-o-transform;-ms-transition-property:-ms-transform;transition-property:transform,height}.swiper-container .swiper-notification{position:absolute;left:0;top:0;pointer-events:none;opacity:0;z-index:-1000}.swiper-wp8-horizontal{-ms-touch-action:pan-y;touch-action:pan-y}.swiper-wp8-vertical{-ms-touch-action:pan-x;touch-action:pan-x}.swiper-button-next,.swiper-button-prev{position:absolute;top:50%;width:27px;height:44px;margin-top:-22px;z-index:10;cursor:pointer;-moz-background-size:27px 44px;-webkit-background-size:27px 44px;background-size:27px 44px;background-position:center;background-repeat:no-repeat}.swiper-button-next.swiper-button-disabled,.swiper-button-prev.swiper-button-disabled{opacity:.35;cursor:auto;pointer-events:none}.swiper-button-prev,.swiper-container-rtl .swiper-button-next{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M0%2C22L22%2C0l2.1%2C2.1L4.2%2C22l19.9%2C19.9L22%2C44L0%2C22L0%2C22L0%2C22z'%20fill%3D'%23007aff'%2F%3E%3C%2Fsvg%3E");left:10px;right:auto}.swiper-button-prev.swiper-button-black,.swiper-container-rtl .swiper-button-next.swiper-button-black{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M0%2C22L22%2C0l2.1%2C2.1L4.2%2C22l19.9%2C19.9L22%2C44L0%2C22L0%2C22L0%2C22z'%20fill%3D'%23000000'%2F%3E%3C%2Fsvg%3E")}.swiper-button-prev.swiper-button-white,.swiper-container-rtl .swiper-button-next.swiper-button-white{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M0%2C22L22%2C0l2.1%2C2.1L4.2%2C22l19.9%2C19.9L22%2C44L0%2C22L0%2C22L0%2C22z'%20fill%3D'%23ffffff'%2F%3E%3C%2Fsvg%3E")}.swiper-button-next,.swiper-container-rtl .swiper-button-prev{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M27%2C22L27%2C22L5%2C44l-2.1-2.1L22.8%2C22L2.9%2C2.1L5%2C0L27%2C22L27%2C22z'%20fill%3D'%23007aff'%2F%3E%3C%2Fsvg%3E");right:10px;left:auto}.swiper-button-next.swiper-button-black,.swiper-container-rtl .swiper-button-prev.swiper-button-black{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M27%2C22L27%2C22L5%2C44l-2.1-2.1L22.8%2C22L2.9%2C2.1L5%2C0L27%2C22L27%2C22z'%20fill%3D'%23000000'%2F%3E%3C%2Fsvg%3E")}.swiper-button-next.swiper-button-white,.swiper-container-rtl .swiper-button-prev.swiper-button-white{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M27%2C22L27%2C22L5%2C44l-2.1-2.1L22.8%2C22L2.9%2C2.1L5%2C0L27%2C22L27%2C22z'%20fill%3D'%23ffffff'%2F%3E%3C%2Fsvg%3E")}.swiper-pagination{position:absolute;text-align:center;-webkit-transition:300ms;-moz-transition:300ms;-o-transition:300ms;transition:300ms;-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);-o-transform:translate3d(0,0,0);transform:translate3d(0,0,0);z-index:10}.swiper-pagination.swiper-pagination-hidden{opacity:0}.swiper-pagination-bullet{width:8px;height:8px;display:inline-block;border-radius:100%;background:#000;opacity:.2}button.swiper-pagination-bullet{border:none;margin:0;padding:0;box-shadow:none;-moz-appearance:none;-ms-appearance:none;-webkit-appearance:none;appearance:none}.swiper-pagination-clickable .swiper-pagination-bullet{cursor:pointer}.swiper-pagination-white .swiper-pagination-bullet{background:#fff}.swiper-pagination-bullet-active{opacity:1}.swiper-pagination-white .swiper-pagination-bullet-active{background:#fff}.swiper-pagination-black .swiper-pagination-bullet-active{background:#000}.swiper-container-vertical>.swiper-pagination{right:10px;top:50%;-webkit-transform:translate3d(0,-50%,0);-moz-transform:translate3d(0,-50%,0);-o-transform:translate(0,-50%);-ms-transform:translate3d(0,-50%,0);transform:translate3d(0,-50%,0)}.swiper-container-vertical>.swiper-pagination .swiper-pagination-bullet{margin:5px 0;display:block}.swiper-container-horizontal>.swiper-pagination{bottom:10px;left:0;width:100%}.swiper-container-horizontal>.swiper-pagination .swiper-pagination-bullet{margin:0 5px}.swiper-container-3d{-webkit-perspective:1200px;-moz-perspective:1200px;-o-perspective:1200px;perspective:1200px}.swiper-container-3d .swiper-cube-shadow,.swiper-container-3d .swiper-slide,.swiper-container-3d .swiper-slide-shadow-bottom,.swiper-container-3d .swiper-slide-shadow-left,.swiper-container-3d .swiper-slide-shadow-right,.swiper-container-3d .swiper-slide-shadow-top,.swiper-container-3d .swiper-wrapper{-webkit-transform-style:preserve-3d;-moz-transform-style:preserve-3d;-ms-transform-style:preserve-3d;transform-style:preserve-3d}.swiper-container-3d .swiper-slide-shadow-bottom,.swiper-container-3d .swiper-slide-shadow-left,.swiper-container-3d .swiper-slide-shadow-right,.swiper-container-3d .swiper-slide-shadow-top{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none;z-index:10}.swiper-container-3d .swiper-slide-shadow-left{background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(transparent));background-image:-webkit-linear-gradient(right,rgba(0,0,0,.5),transparent);background-image:-moz-linear-gradient(right,rgba(0,0,0,.5),transparent);background-image:-o-linear-gradient(right,rgba(0,0,0,.5),transparent);background-image:linear-gradient(to left,rgba(0,0,0,.5),transparent)}.swiper-container-3d .swiper-slide-shadow-right{background-image:-webkit-gradient(linear,right top,left top,from(rgba(0,0,0,.5)),to(transparent));background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5),transparent);background-image:-moz-linear-gradient(left,rgba(0,0,0,.5),transparent);background-image:-o-linear-gradient(left,rgba(0,0,0,.5),transparent);background-image:linear-gradient(to right,rgba(0,0,0,.5),transparent)}.swiper-container-3d .swiper-slide-shadow-top{background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(0,0,0,.5)),to(transparent));background-image:-webkit-linear-gradient(bottom,rgba(0,0,0,.5),transparent);background-image:-moz-linear-gradient(bottom,rgba(0,0,0,.5),transparent);background-image:-o-linear-gradient(bottom,rgba(0,0,0,.5),transparent);background-image:linear-gradient(to top,rgba(0,0,0,.5),transparent)}.swiper-container-3d .swiper-slide-shadow-bottom{background-image:-webkit-gradient(linear,left bottom,left top,from(rgba(0,0,0,.5)),to(transparent));background-image:-webkit-linear-gradient(top,rgba(0,0,0,.5),transparent);background-image:-moz-linear-gradient(top,rgba(0,0,0,.5),transparent);background-image:-o-linear-gradient(top,rgba(0,0,0,.5),transparent);background-image:linear-gradient(to bottom,rgba(0,0,0,.5),transparent)}.swiper-container-coverflow .swiper-wrapper{-ms-perspective:1200px}.swiper-container-fade.swiper-container-free-mode .swiper-slide{-webkit-transition-timing-function:ease-out;-moz-transition-timing-function:ease-out;-ms-transition-timing-function:ease-out;-o-transition-timing-function:ease-out;transition-timing-function:ease-out}.swiper-container-fade .swiper-slide,.swiper-container-fade .swiper-slide .swiper-slide{pointer-events:none}.swiper-container-fade .swiper-slide-active,.swiper-container-fade .swiper-slide-active .swiper-slide-active{pointer-events:auto}.swiper-container-cube{overflow:visible}.swiper-container-cube .swiper-slide{pointer-events:none;visibility:hidden;-webkit-transform-origin:0 0;-moz-transform-origin:0 0;-ms-transform-origin:0 0;transform-origin:0 0;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;-ms-backface-visibility:hidden;backface-visibility:hidden;width:100%;height:100%;z-index:1}.swiper-container-cube.swiper-container-rtl .swiper-slide{-webkit-transform-origin:100% 0;-moz-transform-origin:100% 0;-ms-transform-origin:100% 0;transform-origin:100% 0}.swiper-container-cube .swiper-slide-active,.swiper-container-cube .swiper-slide-next,.swiper-container-cube .swiper-slide-next+.swiper-slide,.swiper-container-cube .swiper-slide-prev{pointer-events:auto;visibility:visible}.swiper-container-cube .swiper-slide-shadow-bottom,.swiper-container-cube .swiper-slide-shadow-left,.swiper-container-cube .swiper-slide-shadow-right,.swiper-container-cube .swiper-slide-shadow-top{z-index:0;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;-ms-backface-visibility:hidden;backface-visibility:hidden}.swiper-container-cube .swiper-cube-shadow{position:absolute;left:0;bottom:0;width:100%;height:100%;background:#000;opacity:.6;-webkit-filter:blur(50px);filter:blur(50px);z-index:0}.swiper-scrollbar{border-radius:10px;position:relative;-ms-touch-action:none;background:rgba(0,0,0,.1)}.swiper-container-horizontal>.swiper-scrollbar{position:absolute;left:1%;bottom:3px;z-index:50;height:5px;width:98%}.swiper-container-vertical>.swiper-scrollbar{position:absolute;right:3px;top:1%;z-index:50;width:5px;height:98%}.swiper-scrollbar-drag{height:100%;width:100%;position:relative;background:rgba(0,0,0,.5);border-radius:10px;left:0;top:0}.swiper-scrollbar-cursor-drag{cursor:move}.swiper-lazy-preloader{width:42px;height:42px;position:absolute;left:50%;top:50%;margin-left:-21px;margin-top:-21px;z-index:10;-webkit-transform-origin:50%;-moz-transform-origin:50%;transform-origin:50%;-webkit-animation:swiper-preloader-spin 1s steps(12,end) infinite;-moz-animation:swiper-preloader-spin 1s steps(12,end) infinite;animation:swiper-preloader-spin 1s steps(12,end) infinite}.swiper-lazy-preloader:after{display:block;content:"";width:100%;height:100%;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg%20viewBox%3D'0%200%20120%20120'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20xmlns%3Axlink%3D'http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink'%3E%3Cdefs%3E%3Cline%20id%3D'l'%20x1%3D'60'%20x2%3D'60'%20y1%3D'7'%20y2%3D'27'%20stroke%3D'%236c6c6c'%20stroke-width%3D'11'%20stroke-linecap%3D'round'%2F%3E%3C%2Fdefs%3E%3Cg%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(30%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(60%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(90%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(120%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(150%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.37'%20transform%3D'rotate(180%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.46'%20transform%3D'rotate(210%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.56'%20transform%3D'rotate(240%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.66'%20transform%3D'rotate(270%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.75'%20transform%3D'rotate(300%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.85'%20transform%3D'rotate(330%2060%2C60)'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");background-position:50%;-webkit-background-size:100%;background-size:100%;background-repeat:no-repeat}.swiper-lazy-preloader-white:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg%20viewBox%3D'0%200%20120%20120'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20xmlns%3Axlink%3D'http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink'%3E%3Cdefs%3E%3Cline%20id%3D'l'%20x1%3D'60'%20x2%3D'60'%20y1%3D'7'%20y2%3D'27'%20stroke%3D'%23fff'%20stroke-width%3D'11'%20stroke-linecap%3D'round'%2F%3E%3C%2Fdefs%3E%3Cg%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(30%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(60%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(90%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(120%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(150%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.37'%20transform%3D'rotate(180%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.46'%20transform%3D'rotate(210%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.56'%20transform%3D'rotate(240%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.66'%20transform%3D'rotate(270%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.75'%20transform%3D'rotate(300%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.85'%20transform%3D'rotate(330%2060%2C60)'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E")}@-webkit-keyframes swiper-preloader-spin{100%{-webkit-transform:rotate(360deg)}}@keyframes swiper-preloader-spin{100%{transform:rotate(360deg)}}ion-slides{width:100%;height:100%;display:block}.slide-zoom{display:block;width:100%;text-align:center}.swiper-container{width:100%;height:100%;padding:0;overflow:hidden}.swiper-wrapper{position:absolute;left:0;top:0;width:100%;height:100%;padding:0}.swiper-slide{width:100%;height:100%;box-sizing:border-box}.swiper-slide img{width:auto;height:auto;max-width:100%;max-height:100%}.scroll-refresher{position:absolute;top:-60px;right:0;left:0;overflow:hidden;margin:auto;height:60px}.scroll-refresher .ionic-refresher-content{position:absolute;bottom:15px;left:0;width:100%;color:#666;text-align:center;font-size:30px}.scroll-refresher .ionic-refresher-content .text-pulling,.scroll-refresher .ionic-refresher-content .text-refreshing{font-size:16px;line-height:16px}.scroll-refresher .ionic-refresher-content.ionic-refresher-with-text{bottom:10px}.scroll-refresher .icon-pulling,.scroll-refresher .icon-refreshing{width:100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform-style:preserve-3d;transform-style:preserve-3d}.scroll-refresher .icon-pulling{-webkit-animation-name:refresh-spin-back;animation-name:refresh-spin-back;-webkit-animation-duration:200ms;animation-duration:200ms;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-fill-mode:none;animation-fill-mode:none;-webkit-transform:translate3d(0,0,0) rotate(0deg);transform:translate3d(0,0,0) rotate(0deg)}.scroll-refresher .icon-refreshing,.scroll-refresher .text-refreshing{display:none}.scroll-refresher .icon-refreshing{-webkit-animation-duration:1.5s;animation-duration:1.5s}.scroll-refresher.active .icon-pulling:not(.pulling-rotation-disabled){-webkit-animation-name:refresh-spin;animation-name:refresh-spin;-webkit-transform:translate3d(0,0,0) rotate(-180deg);transform:translate3d(0,0,0) rotate(-180deg)}.scroll-refresher.active.refreshing{-webkit-transition:transform .2s;transition:transform .2s;-webkit-transform:scale(1,1);transform:scale(1,1)}.scroll-refresher.active.refreshing .icon-pulling,.scroll-refresher.active.refreshing .text-pulling{display:none}.scroll-refresher.active.refreshing .icon-refreshing,.scroll-refresher.active.refreshing .text-refreshing{display:block}.scroll-refresher.active.refreshing.refreshing-tail{-webkit-transform:scale(0,0);transform:scale(0,0)}.overflow-scroll>.scroll{-webkit-overflow-scrolling:touch;width:100%}.overflow-scroll>.scroll.overscroll{position:fixed;right:0;left:0}.overflow-scroll.padding>.scroll.overscroll{padding:10px}@-webkit-keyframes refresh-spin{0%{-webkit-transform:translate3d(0,0,0) rotate(0)}100%{-webkit-transform:translate3d(0,0,0) rotate(180deg)}}@keyframes refresh-spin{0%{transform:translate3d(0,0,0) rotate(0)}100%{transform:translate3d(0,0,0) rotate(180deg)}}@-webkit-keyframes refresh-spin-back{0%{-webkit-transform:translate3d(0,0,0) rotate(180deg)}100%{-webkit-transform:translate3d(0,0,0) rotate(0)}}@keyframes refresh-spin-back{0%{transform:translate3d(0,0,0) rotate(180deg)}100%{transform:translate3d(0,0,0) rotate(0)}}.spinner{stroke:#444;fill:#444}.spinner svg{width:28px;height:28px}.spinner.spinner-light{stroke:#fff;fill:#fff}.spinner.spinner-stable{stroke:#f8f8f8;fill:#f8f8f8}.spinner.spinner-positive{stroke:#387ef5;fill:#387ef5}.spinner.spinner-calm{stroke:#11c1f3;fill:#11c1f3}.spinner.spinner-balanced{stroke:#33cd5f;fill:#33cd5f}.spinner.spinner-assertive{stroke:#ef473a;fill:#ef473a}.spinner.spinner-energized{stroke:#ffc900;fill:#ffc900}.spinner.spinner-royal{stroke:#886aea;fill:#886aea}.spinner.spinner-dark{stroke:#444;fill:#444}.spinner-android{stroke:#4b8bf4}.spinner-ios,.spinner-ios-small{stroke:#69717d}.spinner-spiral .stop1{stop-color:#fff;stop-opacity:0}.spinner-spiral.spinner-light .stop1{stop-color:#444}.spinner-spiral.spinner-light .stop2{stop-color:#fff}.spinner-spiral.spinner-stable .stop2{stop-color:#f8f8f8}.spinner-spiral.spinner-positive .stop2{stop-color:#387ef5}.spinner-spiral.spinner-calm .stop2{stop-color:#11c1f3}.spinner-spiral.spinner-balanced .stop2{stop-color:#33cd5f}.spinner-spiral.spinner-assertive .stop2{stop-color:#ef473a}.spinner-spiral.spinner-energized .stop2{stop-color:#ffc900}.spinner-spiral.spinner-royal .stop2{stop-color:#886aea}.spinner-spiral.spinner-dark .stop2{stop-color:#444}form{margin:0 0 1.42857}legend{display:block;margin-bottom:1.42857;padding:0;width:100%;border:1px solid #ddd;color:#444;font-size:21px;line-height:2.85714}legend small{color:#f8f8f8;font-size:1.07143}button,input,label,select,textarea{font-weight:400;font-size:14px;line-height:1.42857}button,input,select,textarea{font-family:"-apple-system","Helvetica Neue",Roboto,"Segoe UI",sans-serif}.item-input{display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-moz-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;-webkit-align-items:center;-moz-align-items:center;align-items:center;position:relative;overflow:hidden;padding:6px 0 5px 16px}.item-input input{-webkit-border-radius:0;border-radius:0;-webkit-box-flex:1;-webkit-flex:1 220px;-moz-box-flex:1;-moz-flex:1 220px;-ms-flex:1 220px;flex:1 220px;-webkit-appearance:none;-moz-appearance:none;appearance:none;margin:0;padding-right:24px;background-color:transparent}.item-input .button .icon{-webkit-box-flex:0;-webkit-flex:0 0 24px;-moz-box-flex:0;-moz-flex:0 0 24px;-ms-flex:0 0 24px;flex:0 0 24px;position:static;display:inline-block;height:auto;text-align:center;font-size:16px}.item-input .button-bar{-webkit-border-radius:0;border-radius:0;-webkit-box-flex:1;-webkit-flex:1 0 220px;-moz-box-flex:1;-moz-flex:1 0 220px;-ms-flex:1 0 220px;flex:1 0 220px;-webkit-appearance:none;-moz-appearance:none;appearance:none}.item-input .icon{min-width:14px}.platform-windowsphone .item-input input{flex-shrink:1}.item-input-inset{display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-moz-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;-webkit-align-items:center;-moz-align-items:center;align-items:center;position:relative;overflow:hidden;padding:10.67px}.item-input-wrapper{display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-moz-flex;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-webkit-flex:1 0;-moz-box-flex:1;-moz-flex:1 0;-ms-flex:1 0;flex:1 0;-webkit-box-align:center;-ms-flex-align:center;-webkit-align-items:center;-moz-align-items:center;align-items:center;-webkit-border-radius:4px;border-radius:4px;padding-right:8px;padding-left:8px;background:#eee}.item-input-inset .item-input-wrapper input{padding-left:4px;height:29px;background:0 0;line-height:18px}.item-input-wrapper~.button{margin-left:10.67px}.input-label{display:table;padding:7px 10px 7px 0;max-width:200px;width:35%;color:#444;font-size:16px}.placeholder-icon{color:#aaa}.placeholder-icon:first-child{padding-right:6px}.placeholder-icon:last-child{padding-left:6px}.item-stacked-label{display:block;background-color:transparent;box-shadow:none}.item-stacked-label .icon,.item-stacked-label .input-label{display:inline-block;padding:4px 0 0 0;vertical-align:middle}.item-stacked-label input,.item-stacked-label textarea{-webkit-border-radius:2px;border-radius:2px;padding:4px 8px 3px 0;border:none;background-color:#fff}.item-stacked-label input{overflow:hidden;height:46px}.item-select.item-stacked-label select{position:relative;padding:0;max-width:90%;direction:ltr;white-space:pre-wrap;margin:-3px}.item-floating-label{display:block;background-color:transparent;box-shadow:none}.item-floating-label .input-label{position:relative;padding:5px 0 0 0;opacity:0;top:10px;-webkit-transition:opacity .15s ease-in,top .2s linear;transition:opacity .15s ease-in,top .2s linear}.item-floating-label .input-label.has-input{opacity:1;top:0;-webkit-transition:opacity .15s ease-in,top .2s linear;transition:opacity .15s ease-in,top .2s linear}input[type=search],input[type=text],input[type=password],input[type=datetime],input[type=datetime-local],input[type=date],input[type=month],input[type=time],input[type=week],input[type=number],input[type=email],input[type=url],input[type=tel],input[type=color],textarea{display:block;padding-top:2px;padding-left:0;height:34px;color:#111;vertical-align:middle;font-size:14px;line-height:16px}.platform-android input[type=datetime-local],.platform-android input[type=date],.platform-android input[type=month],.platform-android input[type=time],.platform-android input[type=week],.platform-ios input[type=datetime-local],.platform-ios input[type=date],.platform-ios input[type=month],.platform-ios input[type=time],.platform-ios input[type=week]{padding-top:8px}.item-input input,.item-input textarea{width:100%}textarea{padding-left:0}textarea::-moz-placeholder{color:#aaa}textarea:-ms-input-placeholder{color:#aaa}textarea::-webkit-input-placeholder{color:#aaa;text-indent:-3px}textarea{height:auto}input[type=search],input[type=text],input[type=password],input[type=datetime],input[type=datetime-local],input[type=date],input[type=month],input[type=time],input[type=week],input[type=number],input[type=email],input[type=url],input[type=tel],input[type=color],textarea{border:0}input[type=radio],input[type=checkbox]{margin:0;line-height:normal}.item-input input[type=button],.item-input input[type=reset],.item-input input[type=submit],.item-input input[type=radio],.item-input input[type=checkbox],.item-input input[type=file],.item-input input[type=image]{width:auto}input[type=file]{line-height:34px}.cloned-text-input+input,.cloned-text-input+textarea,.previous-input-focus{position:absolute!important;left:-9999px;width:200px}input::-moz-placeholder,textarea::-moz-placeholder{color:#aaa}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#aaa}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#aaa;text-indent:0}input[disabled],input[readonly]:not(.cloned-text-input),select[disabled],select[readonly],textarea[disabled],textarea[readonly]:not(.cloned-text-input){background-color:#f8f8f8;cursor:not-allowed}input[type=radio][disabled],input[type=radio][readonly],input[type=checkbox][disabled],input[type=checkbox][readonly]{background-color:transparent}.checkbox{position:relative;display:inline-block;padding:7px 7px;cursor:pointer}.checkbox .checkbox-icon:before,.checkbox input:before{border-color:#ddd}.checkbox input:checked+.checkbox-icon:before,.checkbox input:checked:before{background:#387ef5;border-color:#387ef5}.checkbox-light .checkbox-icon:before,.checkbox-light input:before{border-color:#ddd}.checkbox-light input:checked+.checkbox-icon:before,.checkbox-light input:checked:before{background:#ddd;border-color:#ddd}.checkbox-stable .checkbox-icon:before,.checkbox-stable input:before{border-color:#b2b2b2}.checkbox-stable input:checked+.checkbox-icon:before,.checkbox-stable input:checked:before{background:#b2b2b2;border-color:#b2b2b2}.checkbox-positive .checkbox-icon:before,.checkbox-positive input:before{border-color:#387ef5}.checkbox-positive input:checked+.checkbox-icon:before,.checkbox-positive input:checked:before{background:#387ef5;border-color:#387ef5}.checkbox-calm .checkbox-icon:before,.checkbox-calm input:before{border-color:#11c1f3}.checkbox-calm input:checked+.checkbox-icon:before,.checkbox-calm input:checked:before{background:#11c1f3;border-color:#11c1f3}.checkbox-assertive .checkbox-icon:before,.checkbox-assertive input:before{border-color:#ef473a}.checkbox-assertive input:checked+.checkbox-icon:before,.checkbox-assertive input:checked:before{background:#ef473a;border-color:#ef473a}.checkbox-balanced .checkbox-icon:before,.checkbox-balanced input:before{border-color:#33cd5f}.checkbox-balanced input:checked+.checkbox-icon:before,.checkbox-balanced input:checked:before{background:#33cd5f;border-color:#33cd5f}.checkbox-energized .checkbox-icon:before,.checkbox-energized input:before{border-color:#ffc900}.checkbox-energized input:checked+.checkbox-icon:before,.checkbox-energized input:checked:before{background:#ffc900;border-color:#ffc900}.checkbox-royal .checkbox-icon:before,.checkbox-royal input:before{border-color:#886aea}.checkbox-royal input:checked+.checkbox-icon:before,.checkbox-royal input:checked:before{background:#886aea;border-color:#886aea}.checkbox-dark .checkbox-icon:before,.checkbox-dark input:before{border-color:#444}.checkbox-dark input:checked+.checkbox-icon:before,.checkbox-dark input:checked:before{background:#444;border-color:#444}.checkbox input:disabled+.checkbox-icon:before,.checkbox input:disabled:before{border-color:#ddd}.checkbox input:disabled:checked+.checkbox-icon:before,.checkbox input:disabled:checked:before{background:#ddd}.checkbox.checkbox-input-hidden input{display:none!important}.checkbox input,.checkbox-icon{position:relative;width:28px;height:28px;display:block;border:0;background:0 0;cursor:pointer;-webkit-appearance:none}.checkbox input:before,.checkbox-icon:before{display:table;width:100%;height:100%;border-width:1px;border-style:solid;border-radius:28px;background:#fff;content:' ';-webkit-transition:background-color 20ms ease-in-out;transition:background-color 20ms ease-in-out}.checkbox input:checked:before,input:checked+.checkbox-icon:before{border-width:2px}.checkbox input:after,.checkbox-icon:after{-webkit-transition:opacity .05s ease-in-out;transition:opacity .05s ease-in-out;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);position:absolute;top:33%;left:25%;display:table;width:14px;height:6px;border:1px solid #fff;border-top:0;border-right:0;content:' ';opacity:0}.checkbox-square .checkbox-icon:before,.checkbox-square input:before,.platform-android .checkbox-platform .checkbox-icon:before,.platform-android .checkbox-platform input:before{border-radius:2px;width:72%;height:72%;margin-top:14%;margin-left:14%;border-width:2px}.checkbox-square .checkbox-icon:after,.checkbox-square input:after,.platform-android .checkbox-platform .checkbox-icon:after,.platform-android .checkbox-platform input:after{border-width:2px;top:19%;left:25%;width:13px;height:7px}.platform-android .item-checkbox-right .checkbox-square .checkbox-icon::after{top:31%}.grade-c .checkbox input:after,.grade-c .checkbox-icon:after{-webkit-transform:rotate(0);transform:rotate(0);top:3px;left:4px;border:none;color:#fff;content:'\2713';font-weight:700;font-size:20px}.checkbox input:checked:after,input:checked+.checkbox-icon:after{opacity:1}.item-checkbox{padding-left:60px}.item-checkbox.active{box-shadow:none}.item-checkbox .checkbox{position:absolute;top:50%;right:8px;left:8px;z-index:3;margin-top:-21px}.item-checkbox.item-checkbox-right{padding-right:60px;padding-left:16px}.item-checkbox-right .checkbox input,.item-checkbox-right .checkbox-icon{float:right}.item-toggle{pointer-events:none}.toggle{position:relative;display:inline-block;pointer-events:auto;margin:-5px;padding:5px}.toggle input:checked+.track{border-color:#4cd964;background-color:#4cd964}.toggle.dragging .handle{background-color:#f2f2f2!important}.toggle.toggle-light input:checked+.track{border-color:#ddd;background-color:#ddd}.toggle.toggle-stable input:checked+.track{border-color:#b2b2b2;background-color:#b2b2b2}.toggle.toggle-positive input:checked+.track{border-color:#387ef5;background-color:#387ef5}.toggle.toggle-calm input:checked+.track{border-color:#11c1f3;background-color:#11c1f3}.toggle.toggle-assertive input:checked+.track{border-color:#ef473a;background-color:#ef473a}.toggle.toggle-balanced input:checked+.track{border-color:#33cd5f;background-color:#33cd5f}.toggle.toggle-energized input:checked+.track{border-color:#ffc900;background-color:#ffc900}.toggle.toggle-royal input:checked+.track{border-color:#886aea;background-color:#886aea}.toggle.toggle-dark input:checked+.track{border-color:#444;background-color:#444}.toggle input{display:none}.toggle .track{-webkit-transition-timing-function:ease-in-out;transition-timing-function:ease-in-out;-webkit-transition-duration:.3s;transition-duration:.3s;-webkit-transition-property:background-color,border;transition-property:background-color,border;display:inline-block;box-sizing:border-box;width:51px;height:31px;border:solid 2px #e6e6e6;border-radius:20px;background-color:#fff;content:' ';cursor:pointer;pointer-events:none}.platform-android4_2 .toggle .track{-webkit-background-clip:padding-box}.toggle .handle{-webkit-transition:.3s cubic-bezier(0,1.1,1,1.1);transition:.3s cubic-bezier(0,1.1,1,1.1);-webkit-transition-property:background-color,transform;transition-property:background-color,transform;position:absolute;display:block;width:27px;height:27px;border-radius:27px;background-color:#fff;top:7px;left:7px;box-shadow:0 2px 7px rgba(0,0,0,.35),0 1px 1px rgba(0,0,0,.15)}.toggle .handle:before{position:absolute;top:-4px;left:-21.5px;padding:18.5px 34px;content:" "}.toggle input:checked+.track .handle{-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0);background-color:#fff}.item-toggle.active{box-shadow:none}.item-toggle,.item-toggle.item-complex .item-content{padding-right:99px}.item-toggle.item-complex{padding-right:0}.item-toggle .toggle{position:absolute;top:10px;right:16px;z-index:3}.toggle input:disabled+.track{opacity:.6}.toggle-small .track{border:0;width:34px;height:15px;background:#9e9e9e}.toggle-small input:checked+.track{background:rgba(0,150,137,.5)}.toggle-small .handle{top:2px;left:4px;width:21px;height:21px;box-shadow:0 2px 5px rgba(0,0,0,.25)}.toggle-small input:checked+.track .handle{-webkit-transform:translate3d(16px,0,0);transform:translate3d(16px,0,0);background:#009689}.toggle-small.item-toggle .toggle{top:19px}.toggle-small .toggle-light input:checked+.track{background-color:rgba(221,221,221,.5)}.toggle-small .toggle-light input:checked+.track .handle{background-color:#ddd}.toggle-small .toggle-stable input:checked+.track{background-color:rgba(178,178,178,.5)}.toggle-small .toggle-stable input:checked+.track .handle{background-color:#b2b2b2}.toggle-small .toggle-positive input:checked+.track{background-color:rgba(56,126,245,.5)}.toggle-small .toggle-positive input:checked+.track .handle{background-color:#387ef5}.toggle-small .toggle-calm input:checked+.track{background-color:rgba(17,193,243,.5)}.toggle-small .toggle-calm input:checked+.track .handle{background-color:#11c1f3}.toggle-small .toggle-assertive input:checked+.track{background-color:rgba(239,71,58,.5)}.toggle-small .toggle-assertive input:checked+.track .handle{background-color:#ef473a}.toggle-small .toggle-balanced input:checked+.track{background-color:rgba(51,205,95,.5)}.toggle-small .toggle-balanced input:checked+.track .handle{background-color:#33cd5f}.toggle-small .toggle-energized input:checked+.track{background-color:rgba(255,201,0,.5)}.toggle-small .toggle-energized input:checked+.track .handle{background-color:#ffc900}.toggle-small .toggle-royal input:checked+.track{background-color:rgba(136,106,234,.5)}.toggle-small .toggle-royal input:checked+.track .handle{background-color:#886aea}.toggle-small .toggle-dark input:checked+.track{background-color:rgba(68,68,68,.5)}.toggle-small .toggle-dark input:checked+.track .handle{background-color:#444}.item-radio{padding:0}.item-radio:hover{cursor:pointer}.item-radio .item-content{padding-right:64px}.item-radio .radio-icon{position:absolute;top:0;right:0;z-index:3;visibility:hidden;padding:14px;height:100%;font-size:24px}.item-radio input{position:absolute;left:-9999px}.item-radio input:checked+.radio-content .item-content{background:#f7f7f7}.item-radio input:checked+.radio-content .radio-icon{visibility:visible}.range input{overflow:hidden;margin-top:5px;margin-bottom:5px;padding-right:2px;padding-left:1px;width:auto;height:43px;outline:0;background:-webkit-gradient(linear,50% 0,50% 100%,color-stop(0,#ccc),color-stop(100%,#ccc));background:linear-gradient(to right,#ccc 0,#ccc 100%);background-position:center;background-size:99% 2px;background-repeat:no-repeat;-webkit-appearance:none}.range input::-moz-focus-outer{border:0}.range input::-webkit-slider-thumb{position:relative;width:28px;height:28px;border-radius:50%;background-color:#fff;box-shadow:0 0 2px rgba(0,0,0,.3),0 3px 5px rgba(0,0,0,.2);cursor:pointer;-webkit-appearance:none;border:0}.range input::-webkit-slider-thumb:before{position:absolute;top:13px;left:-2001px;width:2000px;height:2px;background:#444;content:' '}.range input::-webkit-slider-thumb:after{position:absolute;top:-15px;left:-15px;padding:30px;content:' '}.range input::-ms-fill-lower{height:2px;background:#444}.range{display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-moz-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;-webkit-align-items:center;-moz-align-items:center;align-items:center;padding:2px 11px}.range.range-light input::-webkit-slider-thumb:before{background:#ddd}.range.range-light input::-ms-fill-lower{background:#ddd}.range.range-stable input::-webkit-slider-thumb:before{background:#b2b2b2}.range.range-stable input::-ms-fill-lower{background:#b2b2b2}.range.range-positive input::-webkit-slider-thumb:before{background:#387ef5}.range.range-positive input::-ms-fill-lower{background:#387ef5}.range.range-calm input::-webkit-slider-thumb:before{background:#11c1f3}.range.range-calm input::-ms-fill-lower{background:#11c1f3}.range.range-balanced input::-webkit-slider-thumb:before{background:#33cd5f}.range.range-balanced input::-ms-fill-lower{background:#33cd5f}.range.range-assertive input::-webkit-slider-thumb:before{background:#ef473a}.range.range-assertive input::-ms-fill-lower{background:#ef473a}.range.range-energized input::-webkit-slider-thumb:before{background:#ffc900}.range.range-energized input::-ms-fill-lower{background:#ffc900}.range.range-royal input::-webkit-slider-thumb:before{background:#886aea}.range.range-royal input::-ms-fill-lower{background:#886aea}.range.range-dark input::-webkit-slider-thumb:before{background:#444}.range.range-dark input::-ms-fill-lower{background:#444}.range .icon{-webkit-box-flex:0;-webkit-flex:0;-moz-box-flex:0;-moz-flex:0;-ms-flex:0;flex:0;display:block;min-width:24px;text-align:center;font-size:24px}.range input{-webkit-box-flex:1;-webkit-flex:1;-moz-box-flex:1;-moz-flex:1;-ms-flex:1;flex:1;display:block;margin-right:10px;margin-left:10px}.range-label{-webkit-box-flex:0;-webkit-flex:0 0 auto;-moz-box-flex:0;-moz-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;display:block;white-space:nowrap}.range-label:first-child{padding-left:5px}.range input+.range-label{padding-right:5px;padding-left:0}.platform-windowsphone .range input{height:auto}.item-select{position:relative}.item-select select{-webkit-appearance:none;-moz-appearance:none;appearance:none;position:absolute;top:0;bottom:0;right:0;padding:0 48px 0 16px;max-width:65%;border:none;background:#fff;color:#333;text-indent:.01px;text-overflow:'';white-space:nowrap;font-size:14px;cursor:pointer;direction:rtl}.item-select select::-ms-expand{display:none}.item-select option{direction:ltr}.item-select:after{position:absolute;top:50%;right:16px;margin-top:-3px;width:0;height:0;border-top:5px solid;border-right:5px solid transparent;border-left:5px solid transparent;color:#999;content:"";pointer-events:none}.item-select.item-light select{background:#fff;color:#444}.item-select.item-stable select{background:#f8f8f8;color:#444}.item-select.item-stable .input-label,.item-select.item-stable:after{color:#666}.item-select.item-positive select{background:#387ef5;color:#fff}.item-select.item-positive .input-label,.item-select.item-positive:after{color:#fff}.item-select.item-calm select{background:#11c1f3;color:#fff}.item-select.item-calm .input-label,.item-select.item-calm:after{color:#fff}.item-select.item-assertive select{background:#ef473a;color:#fff}.item-select.item-assertive .input-label,.item-select.item-assertive:after{color:#fff}.item-select.item-balanced select{background:#33cd5f;color:#fff}.item-select.item-balanced .input-label,.item-select.item-balanced:after{color:#fff}.item-select.item-energized select{background:#ffc900;color:#fff}.item-select.item-energized .input-label,.item-select.item-energized:after{color:#fff}.item-select.item-royal select{background:#886aea;color:#fff}.item-select.item-royal .input-label,.item-select.item-royal:after{color:#fff}.item-select.item-dark select{background:#444;color:#fff}.item-select.item-dark .input-label,.item-select.item-dark:after{color:#fff}select[multiple],select[size]{height:auto}progress{display:block;margin:15px auto;width:100%}.button{border-color:transparent;background-color:#f8f8f8;color:#444;position:relative;display:inline-block;margin:0;padding:0 12px;min-width:52px;min-height:47px;border-width:1px;border-style:solid;border-radius:4px;vertical-align:top;text-align:center;text-overflow:ellipsis;font-size:16px;line-height:42px;cursor:pointer}.button:hover{color:#444;text-decoration:none}.button.activated,.button.active{border-color:#a2a2a2;background-color:#e5e5e5}.button:after{position:absolute;top:-6px;right:-6px;bottom:-6px;left:-6px;content:' '}.button .icon{vertical-align:top;pointer-events:none}.button .icon:before,.button.icon-left:before,.button.icon-right:before,.button.icon:before{display:inline-block;padding:0 0 1px 0;vertical-align:inherit;font-size:24px;line-height:41px;pointer-events:none}.button.icon-left:before{float:left;padding-right:.2em;padding-left:0}.button.icon-right:before{float:right;padding-right:0;padding-left:.2em}.button.button-block,.button.button-full{margin-top:10px;margin-bottom:10px}.button.button-light{border-color:transparent;background-color:#fff;color:#444}.button.button-light:hover{color:#444;text-decoration:none}.button.button-light.activated,.button.button-light.active{border-color:#a2a2a2;background-color:#fafafa}.button.button-light.button-clear{border-color:transparent;background:0 0;box-shadow:none;color:#ddd}.button.button-light.button-icon{border-color:transparent;background:0 0}.button.button-light.button-outline{border-color:#ddd;background:0 0;color:#ddd}.button.button-light.button-outline.activated,.button.button-light.button-outline.active{background-color:#ddd;box-shadow:none;color:#fff}.button.button-stable{border-color:transparent;background-color:#f8f8f8;color:#444}.button.button-stable:hover{color:#444;text-decoration:none}.button.button-stable.activated,.button.button-stable.active{border-color:#a2a2a2;background-color:#e5e5e5}.button.button-stable.button-clear{border-color:transparent;background:0 0;box-shadow:none;color:#b2b2b2}.button.button-stable.button-icon{border-color:transparent;background:0 0}.button.button-stable.button-outline{border-color:#b2b2b2;background:0 0;color:#b2b2b2}.button.button-stable.button-outline.activated,.button.button-stable.button-outline.active{background-color:#b2b2b2;box-shadow:none;color:#fff}.button.button-positive{border-color:transparent;background-color:#387ef5;color:#fff}.button.button-positive:hover{color:#fff;text-decoration:none}.button.button-positive.activated,.button.button-positive.active{border-color:#a2a2a2;background-color:#0c60ee}.button.button-positive.button-clear{border-color:transparent;background:0 0;box-shadow:none;color:#387ef5}.button.button-positive.button-icon{border-color:transparent;background:0 0}.button.button-positive.button-outline{border-color:#387ef5;background:0 0;color:#387ef5}.button.button-positive.button-outline.activated,.button.button-positive.button-outline.active{background-color:#387ef5;box-shadow:none;color:#fff}.button.button-calm{border-color:transparent;background-color:#11c1f3;color:#fff}.button.button-calm:hover{color:#fff;text-decoration:none}.button.button-calm.activated,.button.button-calm.active{border-color:#a2a2a2;background-color:#0a9dc7}.button.button-calm.button-clear{border-color:transparent;background:0 0;box-shadow:none;color:#11c1f3}.button.button-calm.button-icon{border-color:transparent;background:0 0}.button.button-calm.button-outline{border-color:#11c1f3;background:0 0;color:#11c1f3}.button.button-calm.button-outline.activated,.button.button-calm.button-outline.active{background-color:#11c1f3;box-shadow:none;color:#fff}.button.button-assertive{border-color:transparent;background-color:#ef473a;color:#fff}.button.button-assertive:hover{color:#fff;text-decoration:none}.button.button-assertive.activated,.button.button-assertive.active{border-color:#a2a2a2;background-color:#e42112}.button.button-assertive.button-clear{border-color:transparent;background:0 0;box-shadow:none;color:#ef473a}.button.button-assertive.button-icon{border-color:transparent;background:0 0}.button.button-assertive.button-outline{border-color:#ef473a;background:0 0;color:#ef473a}.button.button-assertive.button-outline.activated,.button.button-assertive.button-outline.active{background-color:#ef473a;box-shadow:none;color:#fff}.button.button-balanced{border-color:transparent;background-color:#33cd5f;color:#fff}.button.button-balanced:hover{color:#fff;text-decoration:none}.button.button-balanced.activated,.button.button-balanced.active{border-color:#a2a2a2;background-color:#28a54c}.button.button-balanced.button-clear{border-color:transparent;background:0 0;box-shadow:none;color:#33cd5f}.button.button-balanced.button-icon{border-color:transparent;background:0 0}.button.button-balanced.button-outline{border-color:#33cd5f;background:0 0;color:#33cd5f}.button.button-balanced.button-outline.activated,.button.button-balanced.button-outline.active{background-color:#33cd5f;box-shadow:none;color:#fff}.button.button-energized{border-color:transparent;background-color:#ffc900;color:#fff}.button.button-energized:hover{color:#fff;text-decoration:none}.button.button-energized.activated,.button.button-energized.active{border-color:#a2a2a2;background-color:#e6b500}.button.button-energized.button-clear{border-color:transparent;background:0 0;box-shadow:none;color:#ffc900}.button.button-energized.button-icon{border-color:transparent;background:0 0}.button.button-energized.button-outline{border-color:#ffc900;background:0 0;color:#ffc900}.button.button-energized.button-outline.activated,.button.button-energized.button-outline.active{background-color:#ffc900;box-shadow:none;color:#fff}.button.button-royal{border-color:transparent;background-color:#886aea;color:#fff}.button.button-royal:hover{color:#fff;text-decoration:none}.button.button-royal.activated,.button.button-royal.active{border-color:#a2a2a2;background-color:#6b46e5}.button.button-royal.button-clear{border-color:transparent;background:0 0;box-shadow:none;color:#886aea}.button.button-royal.button-icon{border-color:transparent;background:0 0}.button.button-royal.button-outline{border-color:#886aea;background:0 0;color:#886aea}.button.button-royal.button-outline.activated,.button.button-royal.button-outline.active{background-color:#886aea;box-shadow:none;color:#fff}.button.button-dark{border-color:transparent;background-color:#444;color:#fff}.button.button-dark:hover{color:#fff;text-decoration:none}.button.button-dark.activated,.button.button-dark.active{border-color:#a2a2a2;background-color:#262626}.button.button-dark.button-clear{border-color:transparent;background:0 0;box-shadow:none;color:#444}.button.button-dark.button-icon{border-color:transparent;background:0 0}.button.button-dark.button-outline{border-color:#444;background:0 0;color:#444}.button.button-dark.button-outline.activated,.button.button-dark.button-outline.active{background-color:#444;box-shadow:none;color:#fff}.button-small{padding:2px 4px 1px;min-width:28px;min-height:30px;font-size:12px;line-height:26px}.button-small .icon:before,.button-small.icon-left:before,.button-small.icon-right:before,.button-small.icon:before{font-size:16px;line-height:19px;margin-top:3px}.button-large{padding:0 16px;min-width:68px;min-height:59px;font-size:20px;line-height:53px}.button-large .icon:before,.button-large.icon-left:before,.button-large.icon-right:before,.button-large.icon:before{padding-bottom:2px;font-size:32px;line-height:51px}.button-icon{-webkit-transition:opacity .1s;transition:opacity .1s;padding:0 6px;min-width:initial;border-color:transparent;background:0 0}.button-icon.button.activated,.button-icon.button.active{border-color:transparent;background:0 0;box-shadow:none;opacity:.3}.button-icon .icon:before,.button-icon.icon:before{font-size:32px}.button-clear{-webkit-transition:opacity .1s;transition:opacity .1s;padding:0 6px;max-height:42px;border-color:transparent;background:0 0;box-shadow:none}.button-clear.button-clear{border-color:transparent;background:0 0;box-shadow:none;color:transparent}.button-clear.button-icon{border-color:transparent;background:0 0}.button-clear.activated,.button-clear.active{opacity:.3}.button-outline{-webkit-transition:opacity .1s;transition:opacity .1s;background:0 0;box-shadow:none}.button-outline.button-outline{border-color:transparent;background:0 0;color:transparent}.button-outline.button-outline.activated,.button-outline.button-outline.active{background-color:transparent;box-shadow:none;color:#fff}.padding>.button.button-block:first-child{margin-top:0}.button-block{display:block;clear:both}.button-block:after{clear:both}.button-full,.button-full>.button{display:block;margin-right:0;margin-left:0;border-right-width:0;border-left-width:0;border-radius:0}.button-full>button.button,button.button-block,button.button-full,input.button.button-block{width:100%}a.button{text-decoration:none}a.button .icon:before,a.button.icon-left:before,a.button.icon-right:before,a.button.icon:before{margin-top:2px}.button.disabled,.button[disabled]{opacity:.4;cursor:default!important;pointer-events:none}.button-bar{display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-moz-flex;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-webkit-flex:1;-moz-box-flex:1;-moz-flex:1;-ms-flex:1;flex:1;width:100%}.button-bar.button-bar-inline{display:block;width:auto}.button-bar.button-bar-inline:after,.button-bar.button-bar-inline:before{display:table;content:"";line-height:0}.button-bar.button-bar-inline:after{clear:both}.button-bar.button-bar-inline>.button{width:auto;display:inline-block;float:left}.button-bar.bar-light>.button{border-color:#ddd}.button-bar.bar-stable>.button{border-color:#b2b2b2}.button-bar.bar-positive>.button{border-color:#0c60ee}.button-bar.bar-calm>.button{border-color:#0a9dc7}.button-bar.bar-assertive>.button{border-color:#e42112}.button-bar.bar-balanced>.button{border-color:#28a54c}.button-bar.bar-energized>.button{border-color:#e6b500}.button-bar.bar-royal>.button{border-color:#6b46e5}.button-bar.bar-dark>.button{border-color:#111}.button-bar>.button{-webkit-box-flex:1;-webkit-flex:1;-moz-box-flex:1;-moz-flex:1;-ms-flex:1;flex:1;display:block;overflow:hidden;padding:0 16px;width:0;border-width:1px 0 1px 1px;border-radius:0;text-align:center;text-overflow:ellipsis;white-space:nowrap}.button-bar>.button .icon:before,.button-bar>.button:before{line-height:44px}.button-bar>.button:first-child{border-radius:4px 0 0 4px}.button-bar>.button:last-child{border-right-width:1px;border-radius:0 4px 4px 0}.button-bar>.button:only-child{border-radius:4px}.button-bar>.button-small .icon:before,.button-bar>.button-small:before{line-height:28px}.row{display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-moz-flex;display:-ms-flexbox;display:flex;padding:5px;width:100%}.row-wrap{-webkit-flex-wrap:wrap;-moz-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.row-no-padding,.row-no-padding>.col{padding:0}.row+.row{margin-top:-5px;padding-top:0}.col{-webkit-box-flex:1;-webkit-flex:1;-moz-box-flex:1;-moz-flex:1;-ms-flex:1;flex:1;display:block;padding:5px;width:100%}.row-top{-webkit-box-align:start;-ms-flex-align:start;-webkit-align-items:flex-start;-moz-align-items:flex-start;align-items:flex-start}.row-bottom{-webkit-box-align:end;-ms-flex-align:end;-webkit-align-items:flex-end;-moz-align-items:flex-end;align-items:flex-end}.row-center{-webkit-box-align:center;-ms-flex-align:center;-webkit-align-items:center;-moz-align-items:center;align-items:center}.row-stretch{-webkit-box-align:stretch;-ms-flex-align:stretch;-webkit-align-items:stretch;-moz-align-items:stretch;align-items:stretch}.row-baseline{-webkit-box-align:baseline;-ms-flex-align:baseline;-webkit-align-items:baseline;-moz-align-items:baseline;align-items:baseline}.col-top{-webkit-align-self:flex-start;-moz-align-self:flex-start;-ms-flex-item-align:start;align-self:flex-start}.col-bottom{-webkit-align-self:flex-end;-moz-align-self:flex-end;-ms-flex-item-align:end;align-self:flex-end}.col-center{-webkit-align-self:center;-moz-align-self:center;-ms-flex-item-align:center;align-self:center}.col-offset-10{margin-left:10%}.col-offset-20{margin-left:20%}.col-offset-25{margin-left:25%}.col-offset-33,.col-offset-34{margin-left:33.3333%}.col-offset-50{margin-left:50%}.col-offset-66,.col-offset-67{margin-left:66.6666%}.col-offset-75{margin-left:75%}.col-offset-80{margin-left:80%}.col-offset-90{margin-left:90%}.col-10{-webkit-box-flex:0;-webkit-flex:0 0 10%;-moz-box-flex:0;-moz-flex:0 0 10%;-ms-flex:0 0 10%;flex:0 0 10%;max-width:10%}.col-20{-webkit-box-flex:0;-webkit-flex:0 0 20%;-moz-box-flex:0;-moz-flex:0 0 20%;-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.col-25{-webkit-box-flex:0;-webkit-flex:0 0 25%;-moz-box-flex:0;-moz-flex:0 0 25%;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-33,.col-34{-webkit-box-flex:0;-webkit-flex:0 0 33.3333%;-moz-box-flex:0;-moz-flex:0 0 33.3333%;-ms-flex:0 0 33.3333%;flex:0 0 33.3333%;max-width:33.3333%}.col-40{-webkit-box-flex:0;-webkit-flex:0 0 40%;-moz-box-flex:0;-moz-flex:0 0 40%;-ms-flex:0 0 40%;flex:0 0 40%;max-width:40%}.col-50{-webkit-box-flex:0;-webkit-flex:0 0 50%;-moz-box-flex:0;-moz-flex:0 0 50%;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-60{-webkit-box-flex:0;-webkit-flex:0 0 60%;-moz-box-flex:0;-moz-flex:0 0 60%;-ms-flex:0 0 60%;flex:0 0 60%;max-width:60%}.col-66,.col-67{-webkit-box-flex:0;-webkit-flex:0 0 66.6666%;-moz-box-flex:0;-moz-flex:0 0 66.6666%;-ms-flex:0 0 66.6666%;flex:0 0 66.6666%;max-width:66.6666%}.col-75{-webkit-box-flex:0;-webkit-flex:0 0 75%;-moz-box-flex:0;-moz-flex:0 0 75%;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-80{-webkit-box-flex:0;-webkit-flex:0 0 80%;-moz-box-flex:0;-moz-flex:0 0 80%;-ms-flex:0 0 80%;flex:0 0 80%;max-width:80%}.col-90{-webkit-box-flex:0;-webkit-flex:0 0 90%;-moz-box-flex:0;-moz-flex:0 0 90%;-ms-flex:0 0 90%;flex:0 0 90%;max-width:90%}@media (max-width:567px){.responsive-sm{-webkit-box-direction:normal;-moz-box-direction:normal;-webkit-box-orient:vertical;-moz-box-orient:vertical;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.responsive-sm .col,.responsive-sm .col-10,.responsive-sm .col-20,.responsive-sm .col-25,.responsive-sm .col-33,.responsive-sm .col-34,.responsive-sm .col-50,.responsive-sm .col-66,.responsive-sm .col-67,.responsive-sm .col-75,.responsive-sm .col-80,.responsive-sm .col-90{-webkit-box-flex:1;-webkit-flex:1;-moz-box-flex:1;-moz-flex:1;-ms-flex:1;flex:1;margin-bottom:15px;margin-left:0;max-width:100%;width:100%}}@media (max-width:767px){.responsive-md{-webkit-box-direction:normal;-moz-box-direction:normal;-webkit-box-orient:vertical;-moz-box-orient:vertical;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.responsive-md .col,.responsive-md .col-10,.responsive-md .col-20,.responsive-md .col-25,.responsive-md .col-33,.responsive-md .col-34,.responsive-md .col-50,.responsive-md .col-66,.responsive-md .col-67,.responsive-md .col-75,.responsive-md .col-80,.responsive-md .col-90{-webkit-box-flex:1;-webkit-flex:1;-moz-box-flex:1;-moz-flex:1;-ms-flex:1;flex:1;margin-bottom:15px;margin-left:0;max-width:100%;width:100%}}@media (max-width:1023px){.responsive-lg{-webkit-box-direction:normal;-moz-box-direction:normal;-webkit-box-orient:vertical;-moz-box-orient:vertical;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.responsive-lg .col,.responsive-lg .col-10,.responsive-lg .col-20,.responsive-lg .col-25,.responsive-lg .col-33,.responsive-lg .col-34,.responsive-lg .col-50,.responsive-lg .col-66,.responsive-lg .col-67,.responsive-lg .col-75,.responsive-lg .col-80,.responsive-lg .col-90{-webkit-box-flex:1;-webkit-flex:1;-moz-box-flex:1;-moz-flex:1;-ms-flex:1;flex:1;margin-bottom:15px;margin-left:0;max-width:100%;width:100%}}.hide{display:none}.opacity-hide{opacity:0}.grade-b .opacity-hide,.grade-c .opacity-hide{opacity:1;display:none}.show{display:block}.opacity-show{opacity:1}.invisible{visibility:hidden}.keyboard-open .hide-on-keyboard-open{display:none}.keyboard-open .bar-footer.hide-on-keyboard-open+.pane .has-footer,.keyboard-open .tabs.hide-on-keyboard-open+.pane .has-tabs{bottom:0}.inline{display:inline-block}.disable-pointer-events{pointer-events:none}.enable-pointer-events{pointer-events:auto}.disable-user-behavior{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent;-webkit-tap-highlight-color:transparent;-webkit-user-drag:none;-ms-touch-action:none;-ms-content-zooming:none}.click-block{position:absolute;top:0;right:0;bottom:0;left:0;opacity:0;z-index:99999;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);overflow:hidden}.click-block-hide{-webkit-transform:translate3d(-9999px,0,0);transform:translate3d(-9999px,0,0)}.no-resize{resize:none}.block{display:block;clear:both}.block:after{display:block;visibility:hidden;clear:both;height:0;content:"."}.full-image{width:100%}.clearfix:after,.clearfix:before{display:table;content:"";line-height:0}.clearfix:after{clear:both}.padding{padding:10px}.padding-top,.padding-vertical{padding-top:10px}.padding-horizontal,.padding-right{padding-right:10px}.padding-bottom,.padding-vertical{padding-bottom:10px}.padding-horizontal,.padding-left{padding-left:10px}.iframe-wrapper{position:fixed;-webkit-overflow-scrolling:touch;overflow:scroll}.iframe-wrapper iframe{height:100%;width:100%}.rounded{border-radius:4px}.light,a.light{color:#fff}.light-bg{background-color:#fff}.light-border{border-color:#ddd}.stable,a.stable{color:#f8f8f8}.stable-bg{background-color:#f8f8f8}.stable-border{border-color:#b2b2b2}.positive,a.positive{color:#387ef5}.positive-bg{background-color:#387ef5}.positive-border{border-color:#0c60ee}.calm,a.calm{color:#11c1f3}.calm-bg{background-color:#11c1f3}.calm-border{border-color:#0a9dc7}.assertive,a.assertive{color:#ef473a}.assertive-bg{background-color:#ef473a}.assertive-border{border-color:#e42112}.balanced,a.balanced{color:#33cd5f}.balanced-bg{background-color:#33cd5f}.balanced-border{border-color:#28a54c}.energized,a.energized{color:#ffc900}.energized-bg{background-color:#ffc900}.energized-border{border-color:#e6b500}.royal,a.royal{color:#886aea}.royal-bg{background-color:#886aea}.royal-border{border-color:#6b46e5}.dark,a.dark{color:#444}.dark-bg{background-color:#444}.dark-border{border-color:#111}[collection-repeat]{left:0!important;top:0!important;position:absolute!important;z-index:1}.collection-repeat-container{position:relative;z-index:1}.collection-repeat-after-container{z-index:0;display:block}.collection-repeat-after-container.horizontal{display:inline-block}.ng-cloak,.ng-hide:not(.ng-hide-animate),.x-ng-cloak,[data-ng-cloak],[ng-cloak],[ng\:cloak],[x-ng-cloak]{display:none!important}.platform-ios.platform-cordova:not(.fullscreen) .bar-header:not(.bar-subheader){height:64px}.platform-ios.platform-cordova:not(.fullscreen) .bar-header:not(.bar-subheader).item-input-inset .item-input-wrapper{margin-top:19px!important}.platform-ios.platform-cordova:not(.fullscreen) .bar-header:not(.bar-subheader)>*{margin-top:20px}.platform-ios.platform-cordova:not(.fullscreen) .bar-subheader,.platform-ios.platform-cordova:not(.fullscreen) .has-header,.platform-ios.platform-cordova:not(.fullscreen) .tabs-top>.tabs,.platform-ios.platform-cordova:not(.fullscreen) .tabs.tabs-top{top:64px}.platform-ios.platform-cordova:not(.fullscreen) .has-subheader{top:108px}.platform-ios.platform-cordova:not(.fullscreen) .has-header.has-tabs-top{top:113px}.platform-ios.platform-cordova:not(.fullscreen) .has-header.has-subheader.has-tabs-top{top:157px}.platform-ios.platform-cordova .popover .bar-header:not(.bar-subheader){height:44px}.platform-ios.platform-cordova .popover .bar-header:not(.bar-subheader).item-input-inset .item-input-wrapper{margin-top:-1px}.platform-ios.platform-cordova .popover .bar-header:not(.bar-subheader)>*{margin-top:0}.platform-ios.platform-cordova .popover .bar-subheader,.platform-ios.platform-cordova .popover .has-header{top:44px}.platform-ios.platform-cordova .popover .has-subheader{top:88px}.platform-ios.platform-cordova.status-bar-hide{margin-bottom:20px}@media (orientation:landscape){.platform-ios.platform-browser.platform-ipad{position:fixed}}.platform-c:not(.enable-transitions) *{-webkit-transition:none!important;transition:none!important}.slide-in-up{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}.slide-in-up.ng-enter,.slide-in-up>.ng-enter{-webkit-transition:all cubic-bezier(.1,.7,.1,1) 400ms;transition:all cubic-bezier(.1,.7,.1,1) 400ms}.slide-in-up.ng-enter-active,.slide-in-up>.ng-enter-active{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.slide-in-up.ng-leave,.slide-in-up>.ng-leave{-webkit-transition:all ease-in-out 250ms;transition:all ease-in-out 250ms}@-webkit-keyframes scaleOut{from{-webkit-transform:scale(1);opacity:1}to{-webkit-transform:scale(.8);opacity:0}}@keyframes scaleOut{from{transform:scale(1);opacity:1}to{transform:scale(.8);opacity:0}}@-webkit-keyframes superScaleIn{from{-webkit-transform:scale(1.2);opacity:0}to{-webkit-transform:scale(1);opacity:1}}@keyframes superScaleIn{from{transform:scale(1.2);opacity:0}to{transform:scale(1);opacity:1}}[nav-view-transition=ios] [nav-view=entering],[nav-view-transition=ios] [nav-view=leaving]{-webkit-transition-duration:500ms;transition-duration:500ms;-webkit-transition-timing-function:cubic-bezier(.36,.66,.04,1);transition-timing-function:cubic-bezier(.36,.66,.04,1);-webkit-transition-property:opacity,-webkit-transform,box-shadow;transition-property:opacity,transform,box-shadow}[nav-view-transition=ios][nav-view-direction=forward],[nav-view-transition=ios][nav-view-direction=back]{background-color:#000}[nav-view-transition=ios] [nav-view=active],[nav-view-transition=ios][nav-view-direction=forward] [nav-view=entering],[nav-view-transition=ios][nav-view-direction=back] [nav-view=leaving]{z-index:3}[nav-view-transition=ios][nav-view-direction=forward] [nav-view=leaving],[nav-view-transition=ios][nav-view-direction=back] [nav-view=entering]{z-index:2}[nav-bar-transition=ios] .back-text,[nav-bar-transition=ios] .buttons,[nav-bar-transition=ios] .title{-webkit-transition-duration:500ms;transition-duration:500ms;-webkit-transition-timing-function:cubic-bezier(.36,.66,.04,1);transition-timing-function:cubic-bezier(.36,.66,.04,1);-webkit-transition-property:opacity,-webkit-transform;transition-property:opacity,transform}[nav-bar-transition=ios] [nav-bar=entering],[nav-bar-transition=ios] [nav-bar=active]{z-index:10}[nav-bar-transition=ios] [nav-bar=entering] .bar,[nav-bar-transition=ios] [nav-bar=active] .bar{background:0 0}[nav-bar-transition=ios] [nav-bar=cached]{display:block}[nav-bar-transition=ios] [nav-bar=cached] .header-item{display:none}[nav-view-transition=android] [nav-view=entering],[nav-view-transition=android] [nav-view=leaving]{-webkit-transition-duration:200ms;transition-duration:200ms;-webkit-transition-timing-function:cubic-bezier(.4,.6,.2,1);transition-timing-function:cubic-bezier(.4,.6,.2,1);-webkit-transition-property:-webkit-transform;transition-property:transform}[nav-view-transition=android] [nav-view=active],[nav-view-transition=android][nav-view-direction=forward] [nav-view=entering],[nav-view-transition=android][nav-view-direction=back] [nav-view=leaving]{z-index:3}[nav-view-transition=android][nav-view-direction=forward] [nav-view=leaving],[nav-view-transition=android][nav-view-direction=back] [nav-view=entering]{z-index:2}[nav-bar-transition=android] .buttons,[nav-bar-transition=android] .title{-webkit-transition-duration:200ms;transition-duration:200ms;-webkit-transition-timing-function:cubic-bezier(.4,.6,.2,1);transition-timing-function:cubic-bezier(.4,.6,.2,1);-webkit-transition-property:opacity;transition-property:opacity}[nav-bar-transition=android] [nav-bar=entering],[nav-bar-transition=android] [nav-bar=active]{z-index:10}[nav-bar-transition=android] [nav-bar=entering] .bar,[nav-bar-transition=android] [nav-bar=active] .bar{background:0 0}[nav-bar-transition=android] [nav-bar=cached]{display:block}[nav-bar-transition=android] [nav-bar=cached] .header-item{display:none}[nav-swipe=fast] .back-text,[nav-swipe=fast] .buttons,[nav-swipe=fast] .title,[nav-swipe=fast] [nav-view]{-webkit-transition-duration:50ms;transition-duration:50ms;-webkit-transition-timing-function:linear;transition-timing-function:linear}[nav-swipe=slow] .back-text,[nav-swipe=slow] .buttons,[nav-swipe=slow] .title,[nav-swipe=slow] [nav-view]{-webkit-transition-duration:160ms;transition-duration:160ms;-webkit-transition-timing-function:linear;transition-timing-function:linear}[nav-bar=cached],[nav-view=cached]{display:none}[nav-view=stage]{opacity:0;-webkit-transition-duration:0;transition-duration:0}[nav-bar=stage] .back-text,[nav-bar=stage] .buttons,[nav-bar=stage] .title{position:absolute;opacity:0;-webkit-transition-duration:0s;transition-duration:0s} \ No newline at end of file diff --git a/app/www/lib/ionic/fonts/ionicons.eot b/app/www/lib/ionic/fonts/ionicons.eot new file mode 100644 index 0000000000000000000000000000000000000000..92a3f20a39267ae7f45144f412a995a663730360 GIT binary patch literal 120724 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

@FHUiu&=5w(f-3S1h*&)&h2 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 + + + + +Created by FontForge 20120731 at Thu Dec 4 09:51:48 2014 + By Adam Bradley +Created by Adam Bradley with FontForge 2.0 (http://fontforge.sf.net) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/www/lib/ionic/fonts/ionicons.ttf b/app/www/lib/ionic/fonts/ionicons.ttf new file mode 100644 index 0000000000000000000000000000000000000000..c4e4632486d863337c1c73478ddb3c20726c55a0 GIT binary patch literal 188508 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

8E#xmG!w{$tO5=<$!EHPzZXo}IBc1_sqA&q zc&GDdIZndBOtlnbjXypZ_E_E%lLIJ+_L0MTc5a^^8)-GrDt!PySRZ|2WQ+EBj&vRb z{yBWyemm$F+#g&8CW4HBMUcva0RrgC4+j-?3yPOtXf}@>mC?9m_moyu4esy(qF|{2Ym)A{duC+Ani~@&jZ4-&jTO=r>C3TpvA%F`PG3+ zvXaR6QF&ogHAEk|Qp~hjnQHQ~(M(IVV`;@EtB9@vD-A3F6`S@X=D=&qW}3?P=or2t zuewwa>TqRiD7~;p!48|QWGESus(CAjbu(;QyTZwXXI0?T-k?8|+IdFRDg9=TZ8dFk zb~+i1GKV~4<>Y)V+-=C`RT66u?URk$e!i|z9= zb7IkoJy0|QX0DOT*Ba`;LO#9YM&Xc0j$#n6sWrTFjyHAnap zO;zxW-PF99>B)&^qgEvmn^FWr>9Uop?FfiX=%OvNs)iZajX9!tYvu#BgOcH%q*+j% zHBa0ZA*5xQ=DBD(dam;~kZ29{-P$*l@PDjTHt#e4vy_}iFU5NnrdnMrlF9Zb-jbl$ zqSRgN{MXjGz13W}druud>WkPC%9jx5 zVb(EEDCAuw)DesZiRMS`VAMWmP{zlOoQr`q2}UdmIa`)g`4rfZPWMGYB9sQ~T&i0I zZw@Dbs|P`65dY+O9&hNr?g69s{fmxx%4sa`O2l9D>O0Tga^lAQ`*!bo^YWXvEly1g zHwLP?G;Gj21-+aEz3h@|&h~*|4x( zupLZ`gnX*{#Sm{Xv2QB6QK@PrK~#tH29yQ5d2kC=O0H0r4@Cb>GdOtzQlMP z1ovo2o&&efI&vQ_2KDLyr7U%rLBAl-AZTB6e;VeF`T$CHF*TGP!W$#meee;$D3Y4V zC9b{Ms~RFPfA>@+=mAg`RR=8#+2!r5P7-wae6r=|6Dd!{s z)0wFWs`u^!@nxC#o*_l_hD?aNVOW4o)hCq`B@2NkOEV){YX3Oiw!O>L$DJQH%0l%aujq_=U3I79gBCqV~3P;chs_?cXz&}%JIjl)yHCX=j$QpzqW^p#KFBS&Gz1IOJ!$2rAG>nR?OIYS$I3z-R*Jph4KeGEYi}>;>pV}l++YQsu2!Gc zm=)vyW$sPjF#uQ!Y(3IbyTnrWD5LXRqe82`L~5Rpe0YMa17YWAv%~B7ahlT z=?Bg(Uc9)tc6oIb1|O}jx%INZ%_MZpry!;@Et{+cvbB&MyJSJak1l)neX=AZFv5Yz z343Ml+~#MiWa@4)Yjr3cD>ZI@K^JKiC<0}fSY18uI4g_mbLKqbBFn6)f<}C(wak5k ztdA#{w}2Jx7^Xra0>TA2K{R1Smr^Q0YbQcWNf>lU@yH>ppt~oNV6E2IR~xH|m8k6| z@2ir>-sy!SAl($606VQo`YK?0iF!fY(Vj>E-uat)9%bBy6w&}8qy(IDc4BRL!kOrO zD3+XXF0CXUz>Oq)Iu32>II0b?D4Pz!eP!Dwl6&o+Ub=L70=S1g;f@&tTj>fdBoTsD z&}UzCt%uv93T|y*aG8%|MmTl?<7tu{;KbOfC5$Tg1qf4Oje{dVETzm2>-L1K$AnDY z)c!G6_}BdFrPgGv=Aubz!!;CS+NiI{D$xN%OXwg*<_+EpFvTfa%%V{&j+3rsm1f&$ z!b}q?A1#t5oJDmWq;OZeElaa8EZ0^qUzTI5Mmlunp| z43vV=K=2;Y=H*oclk`2kJ!Y(2G_lIWLDhBO+E63>UX5AO);9L)SUC3YT@bXAwi(Y(YuF6DM1W@cYa$3@fm|@# zHL`2&$gUHPxv`vdxG!Wpv%(-G>cVF;)9h87kC{35eJ0mFqBC}%xBj_v>Efyu%9lha zU&i0S|4R;{y7O3+7aLFO@08PO3BNIm)2jSdA=cNGssP(aBBYiPocOG}lzpNG%xz?W2AtJxhtO=@ow#PCWt^f)QL$aIUbM)Kg84 zow!#aG`~JDDTjevM2gEbm(hA>d%<>F7!pAp1?%64hHZAguJbL7Yt&jd+qa3znpl*l z^~1!)i&`yNZ<=qfT%0&NvBE$1c^Mh(>SnHNkyhw4e!eY%;lRNPcrwcB!3tuWX3a4n zc(Xk)!>W5otcc%4GT0mnmE6cdE78x73V0fW$bJ_CFah$bt z$Xx@W>o~2e+aqUu)RhV8tushjt4S5x5jqTcYaV`{^O?CpA7P}T}%YXkU!ktZ0>Jv-8M9|ZRnpQKGaf9 zB6cB`N=Gk5lUj=@|NezIh%g9Q!PRB32?l)^{Goq7wSQ`A|7mFWhUQNu5)ZZ*PBiC6 zdkgb{$CcD31 z88g$96Qf&(8vXTtppQDAEfLxqO=`FKaa{5-*~1c}z22m`PK37jDX(n5Yq#4=x@~?l z@uF^>dCdoLb^Cl%H^n1~gr!?!yH28@$DX0}7}55`S3JhPo;~c;WZnvd7Vyk+9b{)dA6CD}4hAk~s0OL8}8q*Srm+ORILjPWio(^gScU2<%_j ztY!p3}94PdYCS+H3#W>rY+2oCuSE*75{;D?}}+MtJR_Gr_qrHd|t9 zk)v`}CdyTEyr=#=;_=&If9#cq`Ytd(`D@oRZVVc;ttK`>0GMdGl9i93f4bY_6%IB; z#SspM-rxc=N#rVZG3ODZ+Nnb4c%5pvRrqUN{^QpyyjyIkXe_PDR~8xD&bl8)@+aht zpZQV_E1yK#ONiQb{3^l{M7kTF&wszc<_)PvlpdQA_G+SaDlk(^(Xbiv`9g=~81fP9 z>Yfb)S7|DDRAa5E%B3BA>-=L_2F$_@|0WipR#LrWvMmRn1;E=GF z&ni}V|b4-6Q1pkpF=o($b}=-g^ChTi6TNv?C%JuOm~XFIvY-!o~sQg)?|? zX6k#Ne(K4^5y#}FNHRi-rcjoGCfwJavRb@->mX)5GZ^*_B^X~S)TqyGec+zfuDfwD z5*3Y=Z!2-G$}>174a1aaw@cO7(`un9un?27)MG9%o@GVGBnXJXJSiN19RirjOUDkkiRw?j%f0ESCYbsmNwIuZ#gi}T5*Xjpj>@&a(5aVc(av1)QABYBkG^p}xX%5$ z@3Z+1-F+$b%D8qUJf$9DAlvnmWbXu%d+*t3OGYUx9m^9GHSp%hy0GDw?%Yt?Kz(EJ(If{fTNXHa=DiygsecdHd zgOXhC`G4}n6X&0J;wQRF(yq?)YfsBJ=bupJt}i#WVagb9ZRyyBglZ9NpITrd&L!XI~i==R@V6RY(KvShEst8G`l;9f_Y z&$VDdT@r?7n^@4^YpX&3>ytSKeYz5A==(Gi6`7wr7HdmNDIFlZFAadV}_W?NGOem=$Rc0noD; zzAfwsfE4$zoTHsJKE5XwAItM5+KatYP}nM_jiUF~r5(MP2kkw2myFTHeinc0J_vIpxS&J6uGN%n^!={#Axi20|pNY?_d zxv&XhOm>;!NDeFTRav^2nWf_B$Y6h^uQ*?tt&vJjScct|xpH-Jeg5nBU$df~lv=F) z<$4w3^K^w$MWRx!(Pzs3Hxsq$)a)P~k)Q(T(26iyLWBh<__5YWL5?& zqQx&1ZepkE8nLiBXbj@_AF;jaGvQkfMds`uyZ(U9mSn&VPInL3UkM*N6lTz7r)t$n zyFNdx($WfK)|FQ9H}ZW9*}?L-ZmIsg6Ve_VZDJV>iD_$8;vkr0*A^vRwaG9Lq7K0| zF=hpciO9Q`X6zV5@$q3`FpPnkQ_iJuma{y%J_gzUYY3doTH@IdFxMErz3SWcOM2wW z6b;YS`nGSb)$+0mNQGXR)zy1ta}7kscgF9LFD1oM%w8dk@?2zQ0sf(a=v{hmBfXDC zKnBKQtEclVfS@bBT>6rXVd3_@{usW_FulfLzugBMdbRgsjhE5}|!EdPW=_pb8%tap>vmbRc==Yq6vB3#jA-3RFAfP=x zfn(x%I*49OPR||Nr|xVX8yg)OtX0aBeUpM;#F&be%77*s8X zTyzs$pvX+^wA6AGK#zB74)=#&VnPJ}#K*iJtV_I|e4*n$UhEklkU+&!p>X_D>E2!e z3C1i>5{md?UjP!w;-@lwrPP>;_(7;wr#zlgmSn!!tbY(}eP~Mw6rVRz>z}$bP>7(U zXJU3J+}kLFE(0BE*LntL>wVd@qXN#@gPu^R?(;`{DJv3&c>cidxMjTegw8WWusq?f{tr-U_1gB%_)^0F^cfaq?1OaF^&f5dFZh z1=b*8v%+`cf$!wdg#trsQhq68>&AcB(s$eR8w3CNtKJQ?cfR5%V`6*+M5@!QG^1eN zj6PL{?ry;CmD+l&O&uDbt{#yjMpyQvdcEHc_$p1ypU*gfOf1>e@c(ghM}2?{Y=gdJ ztV$$VE)n)^YTpMo`or(IT=mN;sT}r&lg7!`2^rleM?r)KokHBUjA%(naVpGC{E7j6 z}oA&n~d#+BY>zXp&>W-r_n>vIb*?w8St>yN@&VqgnVPtm3 zOs|~9-sCzbk>;Z7dy}n{Ru9&%!dSqeX~)Av0~slmB5fS9^{O6zadR&!XQQW>Im`*M zxf9$)B&DYtkf9Cwh zfAmK_^8WX&yzQ+oJn^UttA0>iJNLkcA^w~__;YmdxsH2w`h=yg!2H2tvEA=`@hy+# z#U1thbI(5W^iyY_d|bQ^Nx&|_K+5u_fXP_?h1nrinB5&ZBH1&yXJ^|)DK^aeP#t}c z>D&v;fw;GX>_Ad~Y5x`x);a@>Ow9G)7_YznPnKGl55Mxt$3FbAb02uuJKz38Z+hdg zqZ8wOL12N9tI0mmeLxZiKbzK+MO&VObPJj`Hp}e?Oe@4-Z^XU>3`K35VLvTryALeI zto>Y(E9uL|Rfh9WyW0=!8W5dzF4?hxlE02%IV3coQljrAo>_-W`PE9rDn_b-M6NgF z?ajmjPB5D3^@Pj0q`wv^nYEhP8|n9_vQF63n~emWU?kh?4VSYC`P!#>|v z!dohNeQ5`CjRUDjDVXsllU&?asrZU8@g%&xCG3zDfYcSf?0q+*_EIICE&W0umM(k4y{NI(2v?|BB?_9#^@Y8il9at6Cz}e?qPir`YS$1@-s!`Kd_K z<`i;2|u9OZ%r9s95PoF;I42@Niif;Kq#?ODz13(S?)0MH2zC(|@1K~$Yhl-WtmLF&U zd{FlwTDowUuE4RFH-=!q+5uYYh;c8|`HiioYDL@dgsg{--^`YATOauP@hxC5UVldu zUN4_#AxLmzh=@?(b&7?2u~Vo71jczWWk;`PN^+I*PhTX?a*j=sDHCi{4Cs7gWAnXN>Cl7RdW-2IsP7~z)poZMD=c%JT ziJFxaRQOy~UIP+Gml~|=t?QKh6tzYMf*R`|TNo2>GKYS1;ogOd;DO~mjA1Z7pH<#8 z)M0iK0JRU}shRYJ$_n4kYCUh@jp*Bwd#VpcLzR$~dWwvVaRa=G6Zv*!cN3THMd z&A_}J^YxRYK-QB^AMK@gjvEiK$o)-gtO>5)J4@Cq&$fQTLry)seC&>KUnF#RpBaqY zytk)7LhYb$VXG}zB}Be3ne78x=BDXJ!D1qYPQ?g83?Bb^ z-cZB{9cjM|=^hLbB;MM8o$Em9BV=bcdLcZZ4?VcJ`1;cio_Xku%e%68eDV0*cOJP- z;@p4?f>@{zFgjA}xzn#69l}c_5?l1=dbKt)$h^i_Ee~JSpQ%oYOcyR}p68_ZWl+Fp zAv!igDhnG0qlI9C`czIn0}y!C@Hh+MoOHL2UnR()Ggx>DI@px%_gMg6ATdlY+7s?* zxlOF!GJcLV%I>ha))4s@Bx7{u>0mVKV%y^iDVYh~^WW;==Qe@p9tgf>UQ%+WZiFiRdjeB~uKCG`^O z5w_FBlA8vp3&-qZk1IHlyaJRgE{Mb2T zS8IDJ0Y*uApl3u1QCeOD1IIQ%#vjad>?03z=%mdvjBF+xqCW}hX4&2a;(;>C;Gu*Y z#amsG(yjc4^F`+`*9*3?Lb+C2f5wNa8%1JK+VEw}8|00i8LIabqLfFR8Q5AjVLCCl zS?WO5!T9dkNJi2RR9J$%M9SzLI@AopGB?*mviKo%%J3}zw2a{_*Eyj2K7DqyF(n>V zLL5Aho2FCsWIdG@rhj~kP`>)isgcpP1^D%6kL;TPJV^vyI33B5FCkRw=_$bM)YDTE z`ha9Qk`0l;R>c!D`;JKAyXw{ykI~fNKt3CC^QY16sQj$PYz<9v2Ozqr9Ug{YpszR3 z3^vKPl2|9f*MXBF7)6Rwxf!ZQU29Z|DrR_+e+bX@ptg(0w~b5@MNBp5+D(FZLzkp!w}kHj++CWvE(@X%(buX2i}!(qZat#*&OwHWU0QhjH%(5Sk`8 z+%J7O3(0e!XRjPQ%pvHx z9gZ_`=I+$L51C#u0KN7LEVUY$Og!#mZO#-jgs{fbaqYE$t<;4~QWi;hZlii;#9=cB z;9gQ;&!lE*WRe<~sn-&#t;K;u6PG70TyT`J`-5t^1?=t6+JB!=i!jVOpeQd+{Pn`x zMOO~E?cvw$@$#35HZnVnF~OS6K$387tWYO08)H`X_&|ihUIM_U3#~-Fy`i(aZ~BR! zc&G38o23@S9)UooqJdZ-)}Kf?q11>*{w11;R1P;c$tPw6h~?O;mz^&inpnLEu+LG8 zaK9~5HGJNn^28_G?&B5uSTLbc5F+H=qo4CDDpzqBas)@ z9av0Qk@kl|g4z?X0MHuL&UUsav5<;v7NnwPJ;))6p{T1RDlO;2Md$Ja(B+mR#hRG7 z>|DG^Aq|9z^Z!}7h$vxPG=-d@T`v3LB_Le7V066{Oo$i&Na~VJ&}Phc=0%G(3o|Q- zCe(utn-#Wo%*clC3=2I@G9)oD!g zIjfwr_v6Y5-*StF*@~D~_Puj|`?qtETW;yTzvKo1>C(gcG34$8V)di!s`nk1Vj;iH1 z>@|-}OdK)yzCk!4-uRYF-#UKS+&?g|-#pyTjpW`t;>KvjA`<)sB^fP>o+d3yyK;2X zG|;Z`XxzrvI8Y($OKwj7X={kw@*SOfy(93|ueSc?;m(a7j=lV{i&a~so)e&e96S>f zM$34z^;j?Q8i?6Um^drqtsr~}z;mMr<6znev5gNta)Aei17aeN7(vKYFrMRw#C9&N zr%mR5Rhd#_?UFS>C4G;g(D+9MFy6 z+uQ)Fy3#viQ2t1bQLd$~Eqr+`3M{Km=qp0AL}~Hy6G_!;&Y{oG_!Bw%tBnh*m-tfy zzx~L=w-xVycx7efg%_0Z0(I+L1KH%}-^D90Ka%q=saEGi&hzSXm$@HFH<)+iPc+W7 zUY7=bP5X+>SG&R5#t0u%z9rzCQM@}!xWEy0MlkFNN@6I(a1%qx1PL4aa=8$cRb(o! zBY$2b1Ru*F|*>z%og&i z#hj@c@A+aTX8Q1tNKD7L_WU77JmmP44LHYBfT;t(&)K8F)X#{~dfpud5n%h_72o&; zF=MjT{yslp*^Y+Gy~V6vDdOR3)Jas5Hsp?E7Mg2HotxSEh?;A|igP0#8ALN9>f==G z=?fR0_RjgY0->{8^XO*{YF-bXFJ{i2%M5@tUR+$3`qFi*;0Cz^&k@sc<$kP_1FUI$ zy|v0ou&4n2TpB2pt#Fnw$Z%k(Ki6x)d*O=%uXj}3i1_1?rCO$B!H^B#muIP(Dq3Ef z^&cdDCD9k{fyE2)+kQzbmX^U26Oz)lzkh>0$=$q7G$(-Pcc-^?A+%1-sZ}f2V^+Fy zvxk5m++h3>6m9oIVCeE*5*K!VF0V-}aQ5JQ=T9|c>|^F>|Ebe!7wn0NR@eXU8RyQO z=YQ8ne2ujIer^1<)-M|oU(^?gp6*pqzb{H?yOG7&NrP8&5O}5C2Crm_RxpY4QyM(g zWBI|XN;~%@3YItKwsSe2N3o}s^$Sb%2utn1b_@S6XxrNL+g952hT8SjTlK7MpSz+( z-TCRDq%0G8H)%Z6dSk{S-eU+%>@X&>J{5*ZfgJTPkH85{;))AoRi#h&(wyI?@>IuY%hy7I2eFAZvhJL#j+9Yl_*} zi*T#48fxoRrnW;8(EweBQzSQoU9{TXlKHnPrV-&asPXKQ8689Io)Xk5Gu5Q;gwoj zrjVLaTT~Eup|H2ZF9bxRQ#*)+shv-1cV(R<>-nqJ>G~D8R(bV$^u0^)IG#uIEE(I4 zbSp78HCo9fSe;tb;ek2`%T!tUV;0uq`t(jKB?jMdVeQB6C!1OFSeSZ;p3Sk-m6SK^ zXZ3z}`M`3(TOFx}o%(D2T+v;AArjZjEGpWA=?{K)oQxk8hye}!});1tHjg2B=8a@iM16nhco zMps?zR+L&;H&Bb~74ua~L)%z#KXkqG##SsrcN%YQy7=mtLm*bGNblgfs{u6Ao3jiXA^l*z&D6x+K!AtXTE@*CI?OEN zpKH=rpj^n)FjwGI-kLam@4=jCvkX=F`i2WeP4Z$Bv2zZDAV}RAju^{Qh;-(otV#MPz z&Eh78cQ(IV4qY2D^n+gyvb*u|k&$}6R@-?KnW3h}31S-=8^PgLAFdN}Q5z&JY$B1J z$xf$HKZWj=P7pKEr0$YU-6`ED;e-D6gYKNkkv@Yi%}D4V08@W3_MK!TxaXy(x;yQ> zeOC9{?%=jx2p#SB_>8cTv*<8k9L<6+|= zgY0CChhD>Bd-y$mmErXf*bhwRgx6Rxo;RMOGw($E$JoSY1AK!Fz`pUII&S<&;{(P^ zoR?=$|2M`5jdx)=wRzuzJ|&suh6$Id8NbahY@?wZ@-F0!-!Z;ntQr@%YnC(O#)9!V zj#c9ic`56tCyi7wohqI(a@A}O$G?xP+x@=C3EH9F*Iz$DJM<;XeX`%nc>NjZwY(_ZkmBAu zhF0epB~l?oP1AWZy;xF3qtGZ8PK>KzqhSv>PJn?&pUB#*i`9zCn$;|)re$oWWo-ZC zrb6~Py{9&nGM(=^t&5rNNuBz~E9cVg)9N(%rDtIpl7;XI^|*RWJ*rNrr}+FSM%2UV zK6QezbxhsP+IvLZ#z?za?NfUhbyKRS3XH)RftDEEP!j&9@js0JX8c#)ZUY%rX5~y9DMp|y`&ff{Oc+3&aH`3+ zyUi!7eWW64#a*fu4>sq#u0a`u2Ua%bu4*E$+1j&;ipy1NCT!?=-JD9-GiWaQcY~-d zKJBDKs8{{+b7&&vc2jdRe5rpT7r;-8sF<45764&DhZZQEYqAosc>82I#~$sP*zx-6 zj3ezb&$7;Q1--_TVUO^^P8oG2I4cVwa=S9zNeUz(E@W96^htg=N{l9e_fSeR<=+X{ zP0Vy50@Q)DK>Rk9g@ie-cADZf^v`mMbT%^!C0U;uGyS?rD7ni3SF6?_SCqCB+eX>U zO*N^vc&Bq&_2|I7X-@TQPk92l(Vj9%7w6*vv)nV93wTo7d!|fte&EhKE88tKotKmH zTe?o#l9!Y6)5_Xjx%2#pQ_gzBVQ;ov%4G9qJ(~;plfPi*v-PAul*`UKBjY7GBWh+B z<@+sRf8qjPHxmBvmU|)egQC#ui|Q9JucUQ|-*fS_X(dzQH^`I03%6wSX+c#Hv&k0; z6O18D%#SClkw_J@7lis-1Kvb{?<^FF7Xrib4ZF-#y6S!w^#p_ecdUnS7JQ{S+hZp3 zAy~8{-k*iHvd1sX8df?5J*y=j|8bxcgtllXJjPX%1urzYo`~0m%1k{x7VxmD2a90c z<3o7#Ed8xN6#0pm&noxqNO?wkrgYQj_M7S%^`v(6lr3LOn%K#0PeXgR>4Ytw^!A)1 zeb9?XoW@Yz`?GbLD3^#@r)bf-6;0%_iy;fDMPixUZ?~kP*6aAPVMSA0ltuH8yA2&) zdrP)Y-t=Y7X!Nt1WaQmI6KlG)c7$_qBL_0-v1BzIsoBkdYDvR|bAeyS4Xg5bU)UzO zGA*n=mncXN6!PAF-jOWpUx$nM(qcoAI)PU0dS}!-17pDwR4Vp}zdn`@@p;VS-xltH zaErz+q+ke%z+&xru_qPsMs#DF{Yp1KYN7TWx7W^_S)m?aO?!^j=to#jzMzV&nR6%2 zSoCETifx~P|#BBw((NOmiM~As5 zioKB_IF<>oEEC7&vap2JG``l+g@v#qo z=$-F)_L(<4_1L4Yd*r?ow;$O$-xwUIF-T40W_5E);GtsF0poU+i+dfqynE{)_B`9=y=z(k$n^^{wn^L4A$Ri$+uF5 zCQAbJOaKyiB2JJc-3{WvC@COHR3)5P3~j%7U;XL;^52-A932)Cj%+KA%D*Fd>Yyy zb&h!VztL}1J+O9GOtifWX>Kp}rb$s~S+Bfi~B>w&=AwwN`fEyHd071{R~CVWn=- z71SIt>v4X952@*$+6!zOf@n4_(T$)=84`u!%+54b}N+#vz%u>lX1??I|$1tV;rJH1a_gkK%J zpb!7i6kVsWLVF2dEsYCt388T4&qt#6U*lpwWZITo5|JvnOt)&2OTz| zh1YXl)SS6-Gl#6HgL|DDtokh!fI4C(u3QAd-vgk}r_ZDdLhGB%{8H8&xnpZ3>zzDs z+x~sY)5+&dL5R+6J21QUHBayUOj=F;>Hc2N>{NQscy@xQbY~>}=@Dn%?6~nSo2|O4 z?>4=v*q9u6Uv|7@=C*Hp`?hWB;6d+HV`l$ky0LA0erIz?)eo!e`_xc#EO+>l51{-p-u~jUbTgF>t3r#ui1vj-1hs)Yqr|e&GdA;iF&WuFbo{* z2*w@AlrcRvg}-RKaVuKtxN?QTA4hjAiznn=< zq!TFxQC1w@V#4%~Kl*N&O#(Jcf0y(MKFyZ!L-+wMNFcgL-}ZrwUR zy?tgoNINL@Fi(yQj8vUct+ysbbC@W>r3+g}fw&f#o&>VS!4U)%HUdY~Y?T^7;n_` z_|SM=qR~o)TqbG9Am$6JP+G5FZd??+2`Eh;FWW|HLE=e!h=T{ zL8-uuyrVf`>BjhIXXXo=H{W3+EOxgCa6RxnC@Wed_9kjjKWO8<1 z62mI{T6~!_ImFpJQ7$FHg2u(_1N-e5`(_pz))4+NJ7r%k9}xM)tc%R+=YQ?wa;-bOY00 zq^I8wLry`Cn5b=Mvx%Mgor!GLwxc1>-|2fg`uWyeHJ7XQ4~}+yR#{jITGRPpFh6Yt zmlpQ#4|?KpPjLSqTfO^6_v|7sO9)JJVAr0}eZ3ZT)O5+`_4-QbnE1^^&bobWwdeaT zatt0Zo@>1sr)u2mi{nm>lZYmE8kXUh5rYNG2t*@@-P1+{M7wp0nPr(!e|U{}(2MaK zJqa$9du=5Y{ztHlM+31aiE%?A>zILa=HTI5I~+&?fe`YZbS2TKH3t$l=5SWos`myu z&eOJa(2?ydtL{-xn>H{)020Rr2i3_N=#;fD>+bxWuD;Zb-0tkZ9rRtNo$u6rV(F9T z_4BP=uW1es4@2WsWnSHnt?GlVm)~=mD9qb>Opo!>4?lk9*1i3ufN3oZ61e^LCr$Ws z7e4gC2bYiEm9zu#)?1!`=1tq?C&mJiU0fg-iU%be6GNF50$hTSWrE2p6IL?(P+mC4 zNZQ5xi;$5FaT_^`_cL_#j-XD6~bI(3~RyJ>A!^304GN}kLNUl#Rw+-jmfw2o? z?UEf_2jD0MEN!5YEt~tAt((k{j{QpmCG`%Ez2Bhh_4GB^tL4OhK|cq7Mk1lnHQvcY1Cm|-&>_eK(F zfT7;+cb1(=CnEPdg;+k8iQu<)9G)T>-WHt5-pKX7P!0u|Uwl@BL4MCmhLLm}{x5E; zFUW0GDxQ`|(oWGf&gc|u(;3Bp-G>_2zNsgc?{mv?^@r_!#|nEZVQ^e*9iA)`+R!W& zfHL}XFy0u}!pP8IzsJ)`KndzmkafFq80pJmD_SAD5exG(%PxB@2!vDV!R*jrb^v~* z{u}L$?jCGUIJJCZ&DYhgmvnD_N1FQ8YRz@srg*Dva+yA|$9$Nzsl73-P$sQ>+cn+G%@s5ax7C zKIrLpCIgXbBrxgl(X*}5(bnj%>F3`{R`Z3~!9Xk)7@RHSt4T0m1La7hJW$9#%UPZO zVozVM`<_!Mv4868m-cYSxttOP+-8;(|mN*1r{ppCH zrCG9q@JNy)&{(qL5a0{FBE}QxuMj!E02)~PN+lPF(#3+Jou_!8$$YL{7ipX5a7GgV zf}PZ4fRa60t(-l3)?TrB)`yi9Wo*3T!*eSuZTp3kPcS+)iIhNyvPblKPNSZL$?s;t z$ttTeaRf_X_btN?B`qv$YAGHi51s|Qiep&FAH&u@&B9U(uw9lNTbr(VgFcto({=Vn zm~$mM0<3FAe7{16CB!nW?^jntS88PihGxKv!ri!H3&KbrfRurP<+1UReA8}ooB47U zl=uzSWM)(cX7&y>w&#a~fzsUe#?aoGzOC{2)`wETU~fE`EbJ`C zV#S^0sEGFlgQ;Bq;IQKi5B96GJ7?#nChK;F9BKXaNtS_~r9z?fhuLC2J6?=={iC_u zsNWkaj^p{vmYhN<94-|c>MHZ2Q&+h4a}cQS!paNyK^bnPtXHelbqajF0=eV)4$!w_ zjFw*GK^L_)2-FkFyiDvh5*Pa_m|svKQn*c-L|dRCJVfyu@E3)l~TPac;X5wwYR5w2OoxFuaDc=!Jb4mnaO8& z_7;jXmFOeuc7`}!*HPocZXM=WIlQnb6EWap-^Hn#bSeaZ*)`e)j39PxR1Fk-0dBTK zCyK3k_m={;e{DYniI=b?I~Ivi`U-h_ATc012}rz-PO?t%q(|kIEzsEpxJC(=`swS1 zTF<7|*r8jfV})?pyuMa0^yDFxqH!|GERFNay1k@*SZli4r*3R-ZH1_u zj0bjParlbS<=%}3ahboe)kKHEuv3kWn%eHYOmk4?l%8kJ+3B5a%hd1ck~Vv)J$qBC zKwqYM=~b8vTz;wFaQif0EcEWo<|%igXE5rI1NPt0G06g^qqN7ByF!lw57@P{h1kq7h5?=+=5Lvkr zYQ{T*wM_(+1xy||H?KtCuAYZoP)(dWx46<;jePNo5vOH4YH{V_%F4ynufFk(Uqxb_ z{aEBfABq%~N29}gq`f!R2l(NPnbwp5%caO9k_Exf?%isM3Iog*7$pT0e0iscz~_^a zjiXa8LG8Q6(M37mE=0Qs7dp3=`-lFj9^m1=e*IxL^?dA-5se8T5^>wd00y|?w2N-t!yN2|JhU!v#jdfvnuu+pQkey>}wEQ9*#pz5YUG7WQ3w8N~&qv4jyxiAl@SXdq z3iaL9S1!MhPG{yb>GWV>uv~6`(%9C}@97Rb7M6_`^{}wuY;Dc5MTPRUZA~DeXGyl3 zgu8Hn?w;vq?b{%h+#msx+%vL@x++1S0r|BJS5~9(QL^M<=-7~favwpapj$JYiL!%x zRV)}++!I!3iKrm=u1;+nI0^(TaKJE%J-MuoglGqSw2MHL&E_I>>=Yew_Eon?r@DCu zJH`7cEzLT#gICC-g0Zu8leCl@qNkHpf}Uc~Vw6PdlQd)S-Q(y)+b6Rd_^pBjnYi3} zT{hp@_9i)}^$yWETp{|e^bS~~iP~Z(srR$L5?Ikq$I{l3kHTcI1MJ*k$&d!--)yia ztpL)tkTfuAFvSFyt}Q^sVrtUYfY2A>769TU_|PCJl0}Xh+8c!5f!7BPNsgz5R5`aL zm8#a#@No(cr9C1KhL0tP1_QEZb1G<5xfM$iua&GJfPEA3FE`_r3J4cf1{n zmFJ&%^BbOi@`*FAfB5l79)DmNB9`NK9=m<C$P4^ zp*D6JyfgYc?=f_@`?K@OhF#A!L0tKZE8h=DZ;Mjo>qNm^c?Rz_ovGLg-D|2tSwt3` z9Zn|WWMA0Q*SDo_Kwf7~<@)-@%6&d3n{_g(f2_=DgIAsPBEKCg54b1Y)cuohbX`l% zA}ybL+M{yWY_9JFH_STwc~>6W-hRzV+{XRpB1P%jauY?#IQy>i`VqP8`Y#5gCSyA& zul#~rblzwb)5h3xt+-7+PmdUCBP0}OhY!iD+_iJt*6AHHJ6zIjG0*mv^0{O&Rm9cW9s{tLZ;XMeI~pV} zO}o!KeckD?&atac+rQc9!%pA5SGV+CZsV(r?x{!hXOHXNQHR{GzOw!qjn?S}-Tbn&t)&157d6CGwOUhB_!H8|89d{$~UT8f#KQTI7$FQmvAP0c) zz!QMVoM56>3fLOx`6$ys%fd~?IU!O*^i zg`GRMZA+&Y_ATt&yJzR_o$wlM+p!I5qx9DF)+B#DdD3~KcSyJ~Y-%+(?pjX+IEv*R zqAw$)#hC1xqy)(EOxE*EQwK0lPD?Pp#1_g!;i>Q>S5kpo`OY`rIq54l`cL;aiWsjm zwvzxCnQ-h3V3fDE7MJ0|OAQSV`umznO;%!k!$aGS9(`Y6pnsCElE!5JK#xDxUq9Pw zot4MnKn$wl?+9*K{_C~YZNENt*OFV}f!4`nJQ9?_w!ofWQokD@S0s7`n+?ccfPe(j zfd!T{Hj*LS=lA;#W5V*^4iw7g-{;1&G)5Xwpl?L2q}Wy_leu`dtBpWLqXyJ#q9E!x zb=_XpRW0E-gyd3br`;kKHrIQlMOYS(jry;Bc)f;us3y7_wDVqm^V6V&TTt zp=vtviLi8%_37wz31SMWeQuSnuBK}Y5?heOn9f3bKiz(GhsKEQq$M?>E{3j|e6r}Y z`1kEjm82S?7hckHJ)&3TA!CAU94w-{WV>Jr`kW{bVS-^BZ)s*F>nPh~>j2xGy7k$G*?W#{pZaLCncerfsqG#egLyNj6dR&1ua`G%z}JknOi@imoH!FphsgmE zGt2|2l5J`3286I%p3bs(taJOfxHmrEb+bp^+h`d`Y9@6Kl&oYeka^4w)9E!KURXdy z)vQx8mkl3Dm6*AkG(1dt0{J-1!7m-^SCFQFDCzGj7xF@zl1jmCpRl9A=`}NoZY?o{ zVe6>+B%ujwJ-cNlG3&lK`&3|lZFPRfT{~7UG;eACUu&1%UP|WeJd^m+<;w?0cI+5A zcyOTET>Hg~7tbeSd4Hwi&&SG6yN^3{$zhSK83$YYNMYd(>Y6}}>pKym%NY$oD71qU z%RXoVc#0$$t%Q7e)&6{541D>%d|x_~lfs8L7v3*+8a);=O=x>~*{iZRx7G{3oCgP) zw-+h?!HrV?CdDM94Fx_gxL#%i^cchG13N=`|0o0e^M_qxC&UQn#PgVt&?M^ zB+>g#l2_txW=|e6JjEX2<-rp|bidG^oo3BuAra~=R%R+sh8l2`Uaw45uXUh5%g8Ih zmQLhweJ(dQ2?yHj%osH!*Rp0eCTd()xDK2HPBgc|a<;#&{vyqTd5}wH;)%$ z#pStU&!~nRk3M7a`ICIS_ND3Y&~SJ)JTns>8jAVxDIb1fdP`tp{+XjEd43c2gwChm zlvBbom+7s?)CxO-nn51KTD5IHX5v(06U$@(s*=+&zoouAK;>GbN7T~Us!?#yk_!?@IW z+DCFUZ2@UGZ@j7XG%nFZB%U}Sxd4r1IFejqZAA=)4G?E!OCSw{5~~3Y5sY=xRxm#X zmNH5bRUYP$ih@8%Mfc6jq|-C=GZLi*;%&GtkgJN*Nx}pOp@NU5YG>hEu1RLijE>Zj zU@QWIAr6u)H0Jvm(YaWR5M3Y#vKcXxO#?tse0IsPCnivec(P@bx6ZDtEw4!Oz;m6{ zftTCgJMIabv$m>Qj?N!gmegL(+NF-Y&ut6gDVR3?nM+@}503@V!N@WhW!ZDuafzS~ z`+{Mu>KjpD?KFl!iQ~c{0z!(AM)DZ>{q|wQw*7Y)q_y8)Y#q?o@kWf(?uAqwi}gR? zN=vQE(9qEIkiZCC&O#Cj)~e+qb~{=#S#eUdCoq^1B9Ll3XjTH?fWu_2*@mvILi{7m z8FEE-nyR&_70*k7)dgoSUdCR^%hbjh1GPzcpX_viLECHGKzX? zjw!z}t^$6~LJeEWZoqX27!v`5N`heuZNN{oz2y&JDkKq-C4p#UM?v~}P}97g(nM*3 z_|`h(h^bucNyI%NQb5N?yhK*jtAjgK&NnC0M{L14c|p|~FpQ%n0!i9IQyAoYDzvsn z1oB5KV^7&TyzkC7Dr1xRP$KcBeV@5`YJY0?XiqknbAW=b9eie@_U2y?9F4~R+?lL> za;Tch&vB=$_TYjn?Ub$+t;@!F(3eeL6=Mx!K8XBMY!_H@;zB^2vz zJ;<$3&43LhpD-;9-WxsUvi=COgzmWR{<=f@Ykx{Ak;&+^L%Q&298~0b#r}*gw$okS zlk3HO>RN>qS@{*7zDeunTInkVW5Px-!dOyKM9V4TD+#HW^wVWz-Z?bsIAc^YR`4nM zG72bR7cL*&W_Ju0zZ6j>;z#kPulA#$OLcoob}18#WrP0q!am zM`Q=0gRE#mT!w)WS}^iskPj4UIqwEFn0NgqYLUvg*T0_&%NpmXOKO>M7dPO_!C}OP z&Wl_IFb+JySmOG!-{H{N@I)fAnMrC@-wvW>i2#5;57MZL7pbZ)U1+U+uk{P+(i)uX zE$VM8hkQr0HSO1Rzm##HwU@mIIfCSlZJg+<-P#soD6N@fiCHf~E9@WIGg03`?jh137e-r+yyxI#HW``h9oj}Uq@}FF2mKgX2)n4=T*zNr00QX?Sf8a{D?slx)YJgC&a4B zuskA1WgQjUr?L)6Zn%MdW`q9INzyv-u#T1KiuVxPiNKOHPul4GQy?%#7w_WAYIKw z9}vh@-_2$qm)r@v-#ubPzDMo$o6*%|svJ|+mO4$INGX|bGA_tN%00`%6IJewK}yrU zpHN|ugM@~V#gi4&-8@eXC8la*Y8ud{E$#`^k%UaAy9zNgr&^&%ap!a}754m^8OSDg zP3JO~Rwm;lJ=XU>?|gvBxwCv1RldUUd6VSr-GfF~Y>O6TI1 z(Zc|iQZaHebb?K(NCu|IL#OEnP@z@%lTSbW$*0%;Tsv^o#PW(-;r-K}e7hE|p-U@r z)%AOWrf&5I8Kk&pOmjg50X+V^mbA_7>7BVEUzES;%IS;CmsZ*$^jnvfJ9XkCc7bm) zR*cWIKK-`mpIh8DKhmfXUBF2!|d6C0}hJilxg-;pe5DKIIp&FTR0kWfH8zsIQbZbJ_#10Yw z4RBS)%QwKX{@A0ZPc@QcaUW_7Hlmv1Kswe1jKDqK_SMYJX;9$oII&ynFU3pBDQrMt zOVz$rj9{i8qh{RXclDVXVI8?iH#Tgt?t|GvjGp3qt=9(UCIM(QvAw`F3azT}jjF*= zlrU4azJv?*z99l*g~wCLpyDV9;DbA2{q#DBj7ln$h=p#8fR5bHD44|6cz~5 zaQjszzlGe$K3sqpF`ye@0@+sJ5e&OAU(&Bi$4>>kuS*;td*!YZ-vY-Q8ff&f3-{HI z1(LC_-!nsusKv(e!E70~rLWOzhmQ1XUxei*^j~+l!hc}P4Cmvir!!ecnL- z53e^B#FJQPZIbqim6_+Z{?<~~Vne*~ql%T2Xw^SJggg->w!V zz2XcqPW~Nv@Cc$tuDZ6$rNk3CaX@T^ojd(H* z>_a7EvE&gB$=Fd_#gefDpZetP)-Qea3m1O!v!DF@r+(%WA0vg%hhO>72VVZ+cRcf^ zvrnEm{m_HU_aDD|@wS`yw{G8k``}=GV1O{k-W+OyHuVz{h2dXt?ofJwsgZyU9RW4q zhUV}QTS_ro!3yGjttqNxbE&^-3LqH$qUx*rB+N?i^AcxO^JTRo2UTv&6o<7ClL^E) zrybaB^p0S6fX-0@)HwzyZ=@7_;F6021Ld;hA(CJ&zJZQXy(L8GJlSOvkX!b~13tnb zBud1}l8z5D?EKpQ422T@K)gh>v@h-p#L>UJ{#X{~D7Yn~dEdT_V_B(a(wBx+F&T|3 zPl@;vxIRMusAZ<3R=Bjhm`xM{5DAjgz=l5}8Ym>P--!YHNp9iMAF}}iF_+nb@eD4g z9FHcwRx#~QL{paKWcsWqX#`>g)3Ranh^GGsx(A8BM|&~_%eOC|Cc$Tas)DxGGa9h+ zsWSd}t8fh6w4Vs;bl%yc8m0a`lV+f|fpV*!NtTIsh8(??h2!qWPtH|h5erO0NX(K} zBvzTbJD3~ak_m;wq0E-?TrdQtG7>>c_&-!U*XSK!*Ou=u)z#q|Jmab_+nWOWR%X52 z7{3*x$v*!+LCA5{VBk^okXYCk_0Vt{0~_=o5u=UZHN_(84;}^96+Cd{wp#V>W4GOR zK+|ekA}5ByR!S6 zSM_)Bn*IC5EBbI#pAB?(RPu`6>AgmOyk&E{BP(P*!)`?`OoJ%rw-`SQtJUAL@%V|| zd&&3}FjnC@dK@OB*Bg((tF??yauhv(6std6Sjd1R+Dj0Iq%XjP^s@0JPG28UcL910 z_#HcOkMXy#CjF%GS>rv%8<9siHTnDx`4)a+dU^qVFU2q3E9b+a^$Tz+!Fb1GHzh*u ziDgRif@{C$8HnnwKoG{(9=@0_V^ zC37=AcDqPW5y?QlMSk)#KZ#z_{3e%WA(vP^@kq@Lx)FCi=%1QX8LmZ;JY}s9OA2%T z%^;6uHt{no_7s%j6Yk-Io=Ps2sb_C8rMz`~I-4)Xnb}MCWeTPLrx>b}Ud1Hu6c%bE ztZaDw{h4%y#B6w?wQ(0ewTXa^_={f;TAsi5<^3f#*JNP?L`5)=Ec?Wl`*tSd&~!+U zDHp_(D~@e)z9&67UpQ)G>nqIlDLbOl5f`98GVbh!Mc!<4%@8s22Y;P|NnG{KPhSQn*k;Lgf=xS>=gm6Dg9_!|YMuhS(C| zXz;15fOW%!I$0hk7)F(bAq1lf=ij5xt6BP}Wi&%ze`(#nAU&C7_DVe)>-;iOu z;2Qi%^{cJPk3V?y$mD3FC!Z$uQxhNfXWw<^bvD+Ox6|P4%R}CU_o>)n<$pqXTim40 zkpx1-hpi&U0FU`kBVSl({Lmrk*pdhMd?BO-TYh3}u^IRl z4WG|*G)xD14nQ>^+|o#}D=!SAC!*>Hx^k8)U!%yc<;sZntBU+uN=vr@cetV?iOn?zp;=+BjLa~R8R#w76dHgt z5DDa0T)Z*)@YFwe@`Hre)7KYe`kH<-m>Rgw1jc|74wY|gx@tSI!KQ@$l!3nRZeekL z{pq{zbim{X-f;HG#~!`?$ZdzPNs}5A!0DD~1RJNdaIAvJ*iE>jLLY=S17Df)pTagw z$^~r0GE+{W8X#8#LUhU0C)&X46?BK!*w>s|K8cB9>(slO@9s(GinUOhRO(s1LW-_V zO3860^nWaNb4|J$_eNa6{j#Ff0H(3aU|p1N_*Vs!!*OPgte5Vu8AY;2$wxaa!E?&S zgy0i1myrbzT{z8yC96+mTmz@9mh$h+XJ*cyp9wftLe>^yV_6*#-byw0H^h~VAVr!4 zO<%%t9(oVCagvdk1;JX5xM_qUD*;1CpaDn_`HR`5UK?Aj{K?uxgR!{4#10)^E(Fzh z5{dm^Kbv-(G{jS0&rm4fM0_?TJr(UO4h) z5>=9&NxM#AsDXDFA2B}F`uG+wEpK|`gAYI$qu%zG7oL6Qz7zM{jq%`J@BCpBE6x}4 z@O*m~mPhF(5_qt}2K=Y-17kpqo)SX6Xhb|7nD@&F{ooMa<@zEf2{S|XFEbI)kAe5HdGmt?;T%Cf5q5@#Zm|%cG1Cl|+E;8% zxxc}Kc*8#;E3(HUS*u*6W0yIvlBf*egRv?gqkz_BYApe93od<8t=Mbloe8nL0sFwF zcXonj+qLO+>Jk(jH+2%^8wNJ;On7{$1~Tuk)R;nR`yPqVf|9dHGBR;b$+TBXxc6$U zUC=jCD-+~@YH2wHhH6!vXAfS6OL)d;wRUV7!RI+p?!})BJ%GR;-N043Cu0WKj%ZJ@;iX1rr{n$okrgxf6 zWkkLhjHI&7UG@IjbUZ#kc{kB%ckO*5{S9XVpzB!Vi}vrbugXsz->RPc{fK8YmFTet z;_+$X(-!9^bGVrocTHY4ZM!^s^Pn^KfBxNcY&0J4-@j{+wvl<#u4fR`%3>=h`NYH| z+onIv35$Rse18J{kk({RZ;0I>swG11`}AMGzsk7+8$!RTjqm4K{(MfKQpBG&y>ZYT&Of5%u-NUG<&+Id|Ug_l%?&X+|?6joz=3 zG_oyQSF&u&wqnb%da^fV8V-hDMO-QmqAt?k1U}%680;p+dQwU(*0n%3V&!5s3 zig%l`unk*NpiR59b=t6-c0t+C@6L=QJ4qL|mHnH0@44rmd+xdCp7T4u$M;8Pm}gIP zaJcv=ba1gqr4-QN|MFLst=s11e|=zaagnT4c{P76Hj*gB#UgHAa>#j;@=U(2HZxxx zhf&n8Ow{#ZxN0mWHr}g~FJH=C5wP*^`l9^YMNy0ht2P=T`jEs?Bh`a_9r}2d066{& za3wr5F<~r=jWxgr2!YdKELm&LwZ2s(rMjE%TC3oyHAfYr;1BPTnp|vn_w8!sXsOjY z#RPqTH3}VoP=;>sfSoo6a}afeQA6;rZN;?19uG^oI{E;a zcC*!L=sD|dE2eo?ed=S{BHt6voQ(eoX%VD&G_cc{u-pk4MC%bTu&hT4B1$m>w*qj8 ziTsGAx;O$N6|f_@!=)ghOB(I3EfuZ7ShVNkLwfOLwW!sZhb}zyNy~%@zYV>(xO{$5 z=N^4%`NBiG!!lzMhYjl+*HR~z9mp@S`jYT(xa7dQ-b8aTnhOqAeURCU&;r9VjF1RV zSM$e4RikimD&hB5D%RaW2Xb>(HHI7F@rJl2M8;{U!6FCo1{ojfYm z=wTML0qs9QhcsZOWkR&!flv<-f)pI-_-Wj$eR zGzzvh-i+&##>ri1Owl~l&hPu)TVK#02!B6t-%2|zoLRom&_GKqU+MS1bYqx!gm=E- z^w0gebEVDfD|MGxp-Cjgc#YE@KzSQPHD-oZHte1p#!Wl{f;KTHbvP`83CCXSO6`tq zsZ^>dMTj@7dQs6@wT_j_%|zT-X~85-s?UZm1pfZ);`ja3c&JuKEmaI?@9*Tor+@Bi zSX{I!X5@IARv)7Vi+Z7E@6fNVGp|vEOmLxNJ?OLtdEpXaTW})@wdXzvuoLWs$EWca z^B|vs0rz+eoZ&oGXEj={+=E*?& zVYk`Fv5l9|RtFHaQo(9i5Jw!sO%BL5M{{4$DvZt$e#1f&7zm32$VYKnkAn4ZLLLd7S@lubuRUscu4-4zL*!b; zGNpC-d^Qk|1+AEK^O#*k10N0~?z=C+rs|$SBODc-J{J5fbRHXVHC@h= zijc%7t*;Qx4=y*v#z6EcD{DjpJU6B{mIY|B(2AvkV3C9a*l*ZXJ4RVGA7QkAf}vrB zP$^-`C9&5mDRzvI%UO+vPoI@&YOVgV-xeOSu~ak4y|%#VYd6~cL1Vbr)?jOHcQk$~ z-O-%&^?${@e_j04mRFf2bJ-!3HxX63#8kL-b1z zu#F*qAmqOukq#mS+q?*JZ$vFk$im#g4r8>zi~Mb5;hW0%WQr1 za)f#QWePfI_N-s0)fIbp?$|m#v2or0_4{$J84{b^B6!BqDPlw&^t#oU9s@rfW)K!k zlntQ9Pw0Y}3a;*~N$BJ2l?W77Xexq%$)Jv3k;sj(acMTtScnmWFl2+zWjY$;6}#PS z^cAuqFTpS#O6iFKzbME?9jX|MnFJ%@(a*yWy=+ zYyXc8|I4)PFVTp!mFPw1@D&)qGOmtdumPJa@a3&x(&1GO0m4Kf;PJW-;VOl&(cQAGdnrts8v*OB!i{!z#4z|v#>HZ-p<7w&4-EbUP695zOsA^Zf9LBUqjjv5 zx)C-`Ef-mc>*>R$*|+?&KGQsGj=X2&#mcVH(OufUtHRDLgjemz?67=$pIWo82PThB z%K!dNt*x8-U2~PnT;=zm%G?1i$aM5rp1#R7`@)mFqD}7Fc{V{O>Oq9*K@vD$Z5(24 zgT*T@4t8-~wbtjz^@LHh$=a;f%I?9@=NHL;(2P_8AH*&_E-eQr%d56rjk#KBxta}q zz2z=#sZUzXqOB)w@3C6vN7nCZ>zLWB*?&tlhxNO|Y7uGFOb6{z9Tyig`e<-`Ce2M5 z7lPPy3N@lOrkb6GGDVUPs{@l(^_;obHdLu5z+bpJ7>_$6#6lKrE|G&JybT7#`JlZS z@iQ)Iz|tLATI%a872{v;oGNAf&cc;jA9-XTw|{8ZKDV$ir(@9#*>kbxVq3GC z=g9ng@5IDa6LE9r$(=ps*ckg0y_Ws)>KZBvpP5&enLE%b(DPWMOnK>TlhW>36oBx6 z-oSw=6KfYF&+weMf`hY#c7YHlaLdlU!dHkdy;LU}_6S;K4ftoZO7*@}Uu5a=IF(wd z;vcNRR9YI@xu(89x7I#&U?uv^ihX{Q+H9IhxUe{DZDT5d?q?E?UUb)3-y!r;*lS`G zgH(aqGpMgw1ya$d_*$(lx1`w+3&@Zny(U3RR##mgJh?M&HQfCC$j-V=|IQJsWyi+M zo}FT#E&WBotB4(Rzu6OyRin2vZHYXIQ>-|s1d0Kvg!n2Lwry=--m$1>!Dj_7ucXBB z&hpc)R9%^ePcNRXc(!i#ly7|G#v6BRI1=^8ubr4X4MgVLh-Z4*GrZ#jXEu-8)^9(( zC{+;NbwDq`Zv<6NB&6yAbK&x7Bk0bgQP?{Pg~WJbBkTj7Xqc!okf2W1HstBVK@3P7 zM0e14nIEW3G~u8k1$oi9dAVpx`bb5-45r!2isX`0Z3>bmAN@a207H4(LA1YPeS4_BTEPgow z7b(8DS=3J5hNz$TPY6DANAiy(%OV3y?|&dFX~Wczy`{aBwRaBZQ`NnWbQ9r-jE2_2 z8v~I@V0kHkV@e<&XiB&bxD!pY!A-13Ss^px)oI~5<_MM`ZtD`tg3nm&zp-eGfWlqHLuq6-Mp6gaHl<6YHzU*P4)4cYAm!i zNODuU!=BZ-OPl%QG=8M5acC&l*IRCjrxMn2+G=N$@6Kd+s>b&AiQq$FYmUDXTuV)xb3>@{^y%(1-gM&)*Ij$;>Z4a(dBxs6yD!_mZF1vSd7v+& z8kNZ{E}AU_2Ew3q#VAN-EPjOXwV=-gey~rE;_fH>oh8|{jC@eFr%n{b$quJ%3M+`>-%M!cSNpzU>+|*!0purz>VodUf$v~;m@7YjvIGUUj>oJQ=O(ovP zDW^M_E`S!>8}=eR0#YI63E7((vwf*VDv-+sQi;B{bhc9N-f=~8Y-VO`cp{y2HegYi zAGBkti4qrUA06%tWddQpi^y=P12Z8#<;;7a0*6vG|9c+!#O zJ7dhNy^{#MLTo31wEnx>OoX*2WUrunaH z+Tu@FTi}EBM>j8hzP2?Fba3@C@~ib{PNR1$Fn5ILDeYzf%{2CE%=s`HubA^=iC8<6 zMjL1le0&HBsw^-@xH9$Ab##OMRQ{B!qkH1u!TSzwE*#&w^?0H9KG*B=uX8kBb>G2* z6Fv7AwjSSFC|*acVV3!EyFjhs}c8Z%@Re&9{B=I0>iVmbIjE=R(3RV?LtM^9* z0=`HUJVh_0ZtbjCw{QKgrFCO-bEoH0saspA&cTV%PNT%}+}JvKY#Ar3IKbl5UQkC( zRtJ6}R)qZ_d@R5?2!xU9vhepXK8o%N1u#-3@d4azhyX*lL&D5z5!z|{wnF|D0M1pN zt`(?Y1hkv#fFp-PaR`wq{?Xy!CWx8nh4!)!?D-SjaxVTbH!X@HY}X=5QG z=DEa7#FG+Z64-r1e59v0Z&;5iYN)TLH4B;^I#5MKDS#q$9`nqq<){qKg%N-h7>&zF zKQ`Gm!13_uR^%+QELYH+#;DOBjwy0>MZEgs=cFs{1L+R{}hH_4Gr zN0Ytl+d{*MYp+d|Tlg6c9o-OZ+Bgs_KiX6-bhYGLUHs63YPTzHtJk66qpK2tHqGV<2agwqqql<8_uI z$#hpFotMReGy@Ky)tRu~SexwChDybWwCx(sgRH@vXtfc(k5vno4ot~MIdFQ_0km}dy7l^_jAQn8>oz3rzeK`Hof34 z$D_lM930_jA`&hPl0TamP9}!^GyCL90(Hv8REH7eo*apI4P;COSR6^hwP?QVbQNTbp z;wnYT8*RH^cwzUP>wDkZ*01N^`Of8~cfNDczM$O;4=w8k#hQ>;m4-OJtpc znF)kZ1O~C6@hVbiw!N^Md*OupJ)Pq*6u==kCk6U-2B`tjlw(-uU9@ zu%I{U_pwS}DV92NC49iktb^Vr+PJs9>HdYgZoBE^jmHjOv2X9rEdVr@d%+6Iq_A6X zv@m^4Ni(iReZTr>QzHyo1w-n7jo%8k+wCk1VYy_lM2O6d!nBF`^@v1XvCO|^gSnjS z^DS3OWRU%e{A9IdBF^=x5pqZd#%LlUN?yi^gv4cG2i{o>!IQNcx!~!l8GeRijJug7 zB(}(QhZ)1{8H;I8*yDHL)e@WJJ-K8J)9l80yBR!N!-fmX#A< zF}2Gn$R9JgKFs95db#0_`%E(smCFO}2W(*KL;x_vY%UorZg&u{hKRaGtJ~db5Rld3 zb8Ih$V~9S9X%(^K0~Y235cOxU*cM=&<-u|s=WCZ8(W(@J?+t};;Yt}4D1}6^)W*9f zyrvA7zgVOU?BpD<;MH}mvew&;#6CKtu2tVN`^U;11-so@DH?wJA;Qh=(6-<{R1bUi z7{0*ecpbZ@M)=$(w{DJM3o%iRWHu?*F%IWEdL4%ygEDkE*v&A~6Tu#597Zo(c?~#J zVMl!4fFK|fqGY$iU5A;#tGA_1G%HdDVu%0dLJpcKLGHckz}&7a(-WIAsHanrmP|Ng z>8GoMM1F#m(dAd2<+8Hjj78^NA zr_tlcxlr8vvKgN=c6u`L01)tgw!uKB#R%wy2afDB@s#r#nNTz;lUb+%p!3VCi`IRF~kF`_vR# zmDB2W^>Y&&o>F@gN**E#4--OjP}{;AbVO|$H$0JztekJT`MTpCOac9_nhnex($pXd z)OiH+VHIW-&VUmMtH|{zFa5roMDt=t^P-S|g&pq0N|-NCIKo0)>5vC`P6&{j-6|B4 zU`-)E9RUwViVWHvC;p?##7FejTW`JnR!qK6-gxBj-0oeM&F-AuJULNninL}TZHWk2 zTy2T6ylg!4qz|A9m537+42}j%vIFSZi1Y$isv?_$Ygi!VSg6aAQxf6aVf$(Zy_5p~ zQSD!1Gh-R+W(~`n7jcZiI3+6N`&*2FKfdv|HpcNp$Y2B&3?>@Ax-nkN<%7DIZA~`# z8YMhx%dMUXV|#!YX1qzdNIdh7YHv%8JU)`L`q@3h8^@m--#CoXhHjXdoxz7vf7<+= zP(GVZtzVx?XY=8FE|+Ye_*gh#KzQr^dN8rYhCoZ8c^}w-rnI$h1w23L3$nOw*PB7`P z?zxPO6797zwlqc~;H^|jL@+Ijh#{5$p|9}p!yg|R`S`=5Z+qL@Zt6buz`L&b$VaaE z$hFt%n_ir~d*{XXiK&V?EJ1WkBrO8J%>3##CJ^viw~{UQV(rJxSRI7|ek$il5*M3A zUkRB@8Qy?fmRtovcvzB~j>_#vxp8Ob1E;!gdO_|@1wQhfyLaAm&(6DNWz6IeR-U!R zOcSfYwC?Rt6Ueh!Ad^RKWLtBi)SBW~3MC)wwiAdhs4T%mavE4sh7cIG zG(`e-?Wzp+Wc}cBIQZ%WDya|Ar(rva_?YR_GFF#B<%0X@bQ#XWd~7vquYK~Ri_$}6 zrKqicvHS;3-@36vqzM)(?gmr1zS4`pj=DO=xbDB-7WMLk44X<-`N&mVhAK*i;6J_De4sJTK#LYR zftOY~Nld+#=2+P7_GKb)1=%P%59gF1UR0g=0g$kJyo3g`hj2WuLNWXJG<~9zfEyA% z+O`M$Ai_#i;gyxD;zzaSs&3}5u)GLY-Rc1uNbsgvyp|IzD*6coQc4L54gI7Zzo)J> zd_SV>t3j_F|6JbR`q8{U|LInL{+Yb5b;0cEF?)sw3Wb4!?id?eerbGMGDkoE_JtuVlM_S$W9w|VC!Eo85?kmKSOssRkpG69$0<{(OgNDSQI1+8rviSe-7 zG9nImjaI=PMW85tkyiP?w8k&c4p#rSBm0>n-qnCQEXc%Lrg{HUW)a6VV@d%LhMFF5 z2Yf0-vW*4~w;$0zq2Pf(16;%d>aR4VB^4Lplt*nlbg(}XE#*ti-E9R>V&mPh7A z*lYYPtq?E5TUSBXMZqBdCFqNYtVboXs3;kb3W@nB*IC>;yCT?Tnq>y%A5*5DYiaSb zfGCZ27kcw80IuQFA4B->Zw`kvssc1I;uW4B zKppN5f$8Z@=X$ceBiU&DbW1T9G&-`y{_(M542f7&w?wkZu*(+GY1Vf}99N%^qh(7p z1iY4k<9m=qTUy6F{+@LLQ{WA@3~uPn<=Wje+-f@ynf*me$0+Mg(5&nRwm=yD!TiXN zWlSawX;<5__QZOKS6wx?aXNR{tFU(judTRFd@J=TQ0oA zqit~vQu=Hq5x&D2A&Ujnj1kCz&di4c@sP;)1yy zh$C3tXmt|~si1@5e@P2*jhKS!eInj)=$S00HX8lH0M>cgu54Ea=IPCuaM0FbZ;|dC zVUQR}m2DQcI7mwjIxSnZ!Qsjvtur`U9(3Z^WkE_f6Qg`jd_BTVmh!G=wPXL?(rV5i zTh|TSo!(!UXZAT9cU_^Mx_f`UIed<Vf9r&`fwN-QJ#dyW`Ex zac2-uv2bb)T#wI}$Yx@FInZ3rF3isjT#I*jq`9MoKr+FAgeW!)JkGp6MvQn)1Km<+ zy10#8DzD!DSza`2;0}1Q5hhZ&(0RUl1begx&2p1~;x*TwiQw7XJW&=021=vtO@+RkTjy{olxGg{{hWutU*tLJ zku3U=$XAfbPau`whFpHPnpb~q{_x7njVNgHS6NO(^eE(*Og1gJbu-&EXn#jz-iB%_ za44?B?TMl-(#%p2Yn@hK&gO|?ufy+I~z%=ENrj*pLx4G#|t^z>9iWz1iF#lG3yvn=^$cFyeFv3+{m zH1d6Ov$+{)j*0P!O&iBHj3I*_u83R1KzRV=dryB)e_!t_f^4KSv2fQau&iQPC^|-2 z-HF^4lmHM9hAIXtJ0Z;d;w_eJlAYNx%vagfX`w(E@Q2K?48vzD;;M z1~*y4gw@@H*xoOOm(PS}*kF;nB!DbAz}1$*N8Q0NAbxEa61RKwqE*cD3zrs9kg_!K zEX0`iELVpgW&d0_e8DNl%7cce~wpJKgRHx6>(dOUyA>`hw_ZlDwoMtCxN? zO)t0{m%({7xyQ|8>$PKC+Xt9gCb9K}4FQ}VmvkIxWa#i>xyC?hi9v-6LEp?ALl81X zUfwY>Vwr2?)jIXIPv!)4w|51J^9P6CM)8~;%hE=~d?Af0K4u>e4#9n7AJ zwc8%Hj(9D@c{x##b4c?6bO4okXhg0dbBvAfB%>Upl8gk_mTRi7;`uPG8!loa8kEq} zIN{ra>qHRf#Y+Z-pyelt3OLoY!8^A>P3a zQj**CMJdYd+#?xm!R0So7uXuZA8>`-CP};m0XE=`q^enTMQUQR|D9ZJvwxa0$aNSO zFd55hb6yk=EgG*q1Go?1N_Z|zb!x+8VaUjgo(=Rx4SawM%m%zKezz6_Bjzn6m&z&7D z7$qTK#<>ZHCdNtzONmjzikFqn;Y&w@B=M&^FEUN4;3I7a#|gY95<6;(SI7vS{#D}u z#LP8QrR#NfGKu}5H{`Y*T|9kyEyKT>&-aU7EewXX` z=9%lSpV?mak1bL&x7GF{4-xEY@z3gKtlZ2P`Jc?Y!DNYQzc=W&RFz`C39ID`V7@sxXhdx>aM)@})4qrPXYOw${~*w_Em#Rr^jo z@p5Z_f2&rl{R25xa2+oz`PK0?d49ijpqit<6}Kkqyp?NVjiKkL75Tb9Y{iTlJNuev zXl7

EqRQu3E}UJ0CHxqD6rqgf+wJ3d;=au>zPEvDWp|n$pd2nxWr|HQ6QIy{+hR zOAB^)6#LuyMCseufI!3Vj(MW6D3KLZVk^v7!7`pL*9E}ED(4$_$QGL_`Aq6%{UotF^2)sh+4d%51eA7A^D7Kek%qq9kbp9wn#6rzFdl-V5Tvh70qxewZqrNfAc%~`W9@qrDa$W7})ED>D{5_ z@B1SWzb=I0vW@c>T>ga4FaLL#*XRq?KWPRS%{6UNjktA{c_mCIMllQ>bO(p)5PSoo z=gY8>R(>zbeB~jQbDrhTK3%H)#Wy}2?w#33n=6xLvib=#eQ0p_pkEY;%#O< zwyjL?E#yOGB5|FYd$otP2Ytb9!SPpr%HZj{QfJ@N(xG8+ zqNBwVU}ik~&;;u34CO~z5-G~|8qHc^MM;+SF)BD@5W4MLN;JR;eG zeE=Mmdf8|P89M8MAfx5)o;wGNQ-2m> zAn+_iUcy$7W@EAGGF(okH2Nn9oQFho?WX%#V-!7=`N3wh0_NE+$3fG!wU8%jlvQ(K z`$vha^lEiw(O5Mk0*DqHlC`z1#a}v;JAF|-R|7E7hSKMhWuxa~4jwZ{;#K!_m%_Ra z#sEDX@<%2m3_f5Q=x~<_<}g}ppa4>qwU8JFGeB5g>jER(FCsx<5tbH~mljUz-07=* zJ9hXgVZbQQX;|3{I^5;ovBO{a=YL*ve;1yQSOZs`<6&@2`i1=@H@AT#E{gz(iY%e2 zYtFCX*Er`*N ztWCw39TbDrx{byVrkLwxCY2P)nH1fLEsaAO{ z@^0$&jVb(|#n{yHOQ%&>sf$h5st;PVa@9!i7gf$t!Ju=sB06);HEZvE1$V5s4VAKZ znH1bUQI;DM;AG(f2U=q!%vWf0iS|QoiUS{8AXKy!jH$T+t0}oP5u-~imaLyj?SL%< z5q4ub+0_*+l{$jW&B5GmanVb@W9!!S+Fbwedhh7M=y$xOqa|z1I00gdnkSl&_(3~) zG$*n=;3U!Q2v5wh-6cMXJY3aBk^ZLi)=UP(@j@n_X~htyHA0+l@p~3&g(&?>VuwwL zA8|=Uv*;{|H$6b0NJ-`2MqQ{{tENuO%+1Yo_`OXz)66w_{T&jDF5tRC#WWbeN zOu7O|&)Yv34u3EO=2^PO&Qd*R?@4<-xqq0=!&H)_hOz^AyrHt|{h#Tiq*~5m1DC@REhio1BMh`Ax*}R+!M_Rq9 zwp`AucOEvp>DHAT?H%P*IP;;A_HZ$@xue_|YW_&MqahQnt#t0bcebzHi8?ah?eJ`O zV%g$s?;9z&BLkD+$$_Y=Fw)oVGAPrz-Q(!aJHhpKxAzT}Rx@nx@HY&3CjA{A|G@J1 zI{ZxoUen(Z@C->mz}dF8M)hGg+rq5afi=QNtryH9O*nw>#c>#$+tgj^^X6wxViR7* zCb_-Xn)Q1f*NqJ}qSdwR=2yPi39YJ7Zjw7>t< zZKrNKal^6s!-r;x3A|-`d~zH+(a{Z~2$1?m`bRo&=|v9*CcOL$XJ8W7^p1$7B^?xt zfn+&`4Z+ALFWJeUSV^>3oGy9gSWDAC=rHshPSH7_a>8kypbL;6=@+@!r^i$c)osp% zb%hkxUb1qvuXGeySkK*ZdiRvum~LunO(b*M;)U!Jjj3RA5T>mu(`NhMhO42l&u};7 zce;$}LZJ|E$POlhnRxL{iBL;4|CHOAT!$4!C-g{`d9e-J#y~gK4KR9q!HYJDVPyHs*b%!`PMV zb^D5XCei2d7Ji!VTi+YPiyJ#zTa!2?(B--|mWg1hq2VAKHhde0N~V6!QA!nF?SMESA#&2B+vsyo8`&wdrBqDIH7^swEr@g%80k zyX--+jAd$-C=E3|QQF*DEe!wVe}8EQ&Fp*L^Pcy<2kh&6??HGrKeKJibbp_Ob&19k zsd#iHt~^*S7K_WG1@k70W2`%u!T4I9Tn=2iM#?gGGyYusLo`4l*Di9c54%Xe}2qYwTU?!ND?BT=6#5OMk4 zVXqrU4R^vF2g%w9I08-(4Ds>s+r4pj!V_>4H8l!`sWdQWh+`7%G?f|vD0KVWkX!8hySb~OLC z4erpVmS6tp`W`djh@=9=(%Ap(OMWWoSx**2w-1S)by$->m`??Zd5W_Ii1t7^}qS`M?ZA) z&3ebi;!vR_q!)6_Kk+XvJ^JX)H^--18w-WT&>(yRh1ccJQQD`M*XVM>WYHCzS*5yHcxEau&!^UZ)9k&yQ|dh0h577EXnE^(Xaeg zMw2D0T_r*Ok+g}HsD2d6^3sSWD`XMPIFdB<+Oh|H@DQ5KTO47L6VnrLxd6{F)G+kb zpx4GennZ-%F2dRXMO&5h51gjgpZ*yNZKj8kCJ>hJ;%<_cY^|(nc!+*~Jm!!3qmgC@$W=roBI;1e<|^47 zrGl%F*0`^of2Q=__m-YH-}jl%^y#tm-RX{ng^%s)(hFPudT#EoubW%GFh8%u7skeP z!}9aMF0x?TChVL(h9U}lG%w>j61G9qGH54jw#Tzp31EO#1F>{}Gh8MsyebB2~b31lhO)O_kHpj4sDM zRiNWcVhlmcOLt-|Ko3JCi$g4tGU#6bgreAUiFCuIw|U&%UF~hHSxa0Pa7R3%l#8tB zC@Oguif&&>)c12y>IFG@r^scVMFvQ&%650 zT`C86wOO4qZye|cSQwx`=jLW!b^#6tgzA`dsHyeZoyA&jvKkHqixJ`*IZ=IM1^`$v z!ZVg#A~+>*2W}&~+1N!`W;eFwTPG(rj*npr)Y_NtYv_zcy@;Gee!?^&W?|M>#3wQc zk}1ziI5oHzqVl=;+5}Uql1i2Y2V5*jtdp?Id{?dyS#9tywrghN1SE*KuMgxNlbEBn z?*&7kouO8?~rasei0sNrAKv%S>DVj`nX2tfZ_MJ4sllH5jnvdcYcqv||Hk#`( zf8Viv>y)`^94=yj_z{Jsm<)fi>Oy*hyq?i!O+2-0uetgtsGR%lGR(+gP}GH%=1>-h zsQgD!9b#)|DG>RvgzHaQ=_6RQGc8+rLws{I6&GV3L+7z}F~PxfzHVSu57efQyWH{1 z6K+ppPaFmKo`lDp*n1qiFoc{PcWlbSsvEAH%XUM{cAG1Uh@*50AwTng+tygJ`H!?C zFm3^Ay2Wk{Cd)sx#;F(W?mcctV2tT2I2v@g=RA(!sLW+!xcC_kV(b}Bn>rIodWIu` zu(`D(eqsdkx&#&yP2r}Lpi{6$XwsK$Y68KvmgTF~fOWMGhEBtv+(EB2uT>}qQj=z@U57PT7Q5_{0yS^7ftLEq;%Gdy7JTZvqyj&vtktgh$s_}BNnsFjbeLM%n{qRaa^mbWo>iS!2|pE?b$txjl&FU(BKEP+uVdIH;k_`xL_Ofh9;OXEcBXJ3Qn7SID1uwoDekfj9r&hBDsIG+y-wH9{|mK-B#Ji$n6*)}jR;LkTBYPh3GcJGiLz*tg!KW*xAb9b7k92qRS zXlFc5=;tUAh}VMA3GT2y-1S^eA}xt5d!>V|#hE}FoNdn2d!mIxL{MNbPI~nxr;%3M z=WW(QwT*de5A{T)>>X?C&A-j{z}bO7wZ>^`?7PEy4r5^L_aIn=jS2+26pI(TV*+GZ zhoHICYG|o@_-KLq7wNlm4+1Z{a|;-wjo!vM-Y*1oPIO0BzED2hYFD?uVa)Q^e4eKQ z(%;VpuesJaU>hBA>c!QM^o#EsD!)IIxva$J%iS&6BJ1x(hKsn2Bw-&x6vR3N)=eKo z#A4xvyy6>mI2>^Y+PjDatY=jVizWH=tS?W7lxmG4_7?)b?Hn1k4LGm8CV19ft&0u~ zy^k-iv@4x?Ki+~CZ&UMo4yg=s!|T<9=KWjI?rqUN&!L?JIP&#l?rOhkKseh3uiVKZ z-l-=Eo(Q*&`5u1KFoYflW-BP+StkOf1o$Pkgjzf<8DcDtOWR&Q7u&XNpJ{0hhmRgU zc;&9ynd`S-KRs0jkFTi@EfuX?odNx!*V&B zNhCT70U$$~vn5xybDW6tu~H*&W8vm(lQ)+iEXHz$RAW=|is{KpG1T5Y+}Ltti$3tR zu4LS3u{WkC{`}7$aBp(9v_I=^O15WG2|RQniKbvMlnbUCN-gnJ(CcdoCo$N|dBb5d z5(|c6!Q9rq@x(-;JskJtr+ch8$)cyI`lb7s$9J2vM3ypgVS<|2vkty7n}GC)&72+R z4(w(b^3dZ6hq;@LR{QuGwVDYAl)`s66N?18gWar){e`@TMabG_t<*lb*J!M>EA8_d zPk&oi1#zLp)27yF(!zO6kH`yET<65a%SuiX+$j38TAH3-njamV=Wq4feGH!V2DNDVu0rnT z*}}5Nz_+A<`L=>;)!}i^Gu9v|Apk;E!HP{RD9%-*6l4NOyQ~)_j2BKOyr#4{R=rYY z>R;~CgJ$xE>yI2dxPR{+qCFz76WEESurJb`WPQX^TpT}>K%<~D!F;Ekj~BL#BsoT} z#sQxPCpaV%CrH5>Qj9`5ooFGTn|wEdoyWIj*(!{*7xL0uEDNB*A4T0G*k%#(yTrQh zF~i{h2W>`2G!9$alnNy~BashR|L1y3_JqS|$n?jG;7I3p@N2X{xnj*14A`$|IRmf{1)8Eb*#&~dra&;l z-s{`fccq$11Ek6NI0=&!_Q#{msjdxe!NyXHz2E6%p10XrO3VKk-&jl$qK?4|IATw- zZBq>8g}f6N^WXwOkoh);4Ktn1Bn0MSI$ARu=nzg5<0Z2_FhHk z!W)gdIgyYURZY=#*}TViLqu}S z2Rc$C;ilFgf32gfIk3WP`Nnoz;Tg(~M)(Q{dd^&PvwFXJoCtHDQC|`ahO3y*zxesH zpZLI|zxLKQ-h1ckXpn(YA)87Bm~$FoU#31J7Vmgbon&4Tk%veYZ?x1++rB?bT!#V{@gXAJF*(z4PpsefpUL?4f6@H8_ z!%7=1r&?^~qzr%*X$3_~Nip(ex<*q;XNr}mB*-n~L#yu*C)_bw3L+r=3 zd!6pIuQ}`VhJ)j-;TBh9JU4f=z0K)JMYiuGj3VL>7jdtWEz^E9nJ4xc=>exL9kl~u z#Ds|daMI~c;Q#HjN8EP98))W32vHMnc)TsTe7m;ebEw0!ya!i5^j4m;EglVieOx1 z8|UGhuT#&e^Xf(__1vGHvgL!voY*y86$;owt-CiDS{ohV(5K#E62q+x35s)S zN=Ni0O>49oy7dG`pEe9#!Vw2yR1?WqG7-c7q6BhkA)pSm=|X3FhpiYa6c6QOd5J>} zBFwyM4dq)8Wp#)UP!W_-IO#Xw=IBTs5*rR1P;a)F(}vx>GnKOJuTy(f%9+~s;~)Rv z2TJ|;`5!<3H-G(uAN}A*Km4ob{^C1-{;fax-LHJ%^Pm09r_Vn1vEO;}w}0!Szxm-0 z{rdZ@c&lenslD|rZ@z!w)?044o`=0|{^}!#5DsowKUN;HD;NCQcW-B2$#0oAoJ&C`k-kE}X2Hx#YWLT4PGGt`)a6vFfKBTukH)T3Alw zOTlLf78>PQQ<-Gq<}xoL%0#bEPQEaB{-jHc{D+_mm8}9<8;=@z47rV#P$$ul{07)? z;57^WIKtNkcRXls@HaF-_F%%*KpN{7E^}h979@^#U$isS0=$>sVT64Fn|5t|!-FLp zmN0m5+d+CV;-CTm+Exs=hnzkPy$RifG|my>E3xUIC*@dTNfxuiF<=wZ-Pwr^NBT*j)l5AQH6&D!F)@f0J`coV*K z|M5K&-R^6C2z6`k6qAH}hRbOU7!L;Hrrb`tNNU77uJw^c#^-7Th(`5pGYn?E9qpzA=SX^;t_5oBg^kn3 z)$jy%V#y-9bNRhK!l)Ax2=NC~)MacO8UO;05Ki`x-w?^oFOvPB*^o>|qshi3%l2r3 ze^JayaEc40Bsh-?h1*CW;;`ZY;qNtqsvXJ+ps8uj6C3EVdQ8 zmM@s*-2MDfhpN1_`kchpR@=;J^f>ZFECOp474B3X3vcM}wla&gYSiknM0=M(#=1gmVlPK8+m%Zv;}Iw0C5X@H5~lWO z;!Pm)LN6t-aHs_Z1{TGWi05P}W7xN0^|O8RB*y9kaAGm$5&ApJL~9bMs!}W|er*z9 zDd?yzEQM#aWHQW)%p;cg(;D6_!yTkE0`u}2!{rh+xoC3Z(I%Ld5Iw?{1MCF`y~(3n zioO(!1?2rnO@t!cMc?%3x=I>t?#6(>d*HIMO6B|)?!W&~rF?fsPPaSaVUg5Po->YE z1CN#n(2clWIt*tVG;f#}#{F;@G@a(=#)NBkr7}C*69}%0*@ex!Y_@Xc8^7?zbm@+f zafz$6qB9YYMv#^_4YwD;APsq3GWSmrpbsQO1EntQg%=7NRCdbP*4JGK+iFTmra9|T zVH;(kTvIMpmvgMCVHt-d6>C<1mw!2~Bj2xc<&0}}7;1Y#5D<)|@ z47J?Cyd_aG`NNTvuh^nJO?q+VIX)T3c7`Tk%D{}r9w2h8Q=HSm*bT=WK3Af#x!LaU zi>H+%gqAo|d+r7LPXj&Ze)GN*viHGuLJjhlNiTig9G-5{~+e+LG2?z9pd$n z$oN7cs$|kak{mu517b_tKqW!YZimrS(WrfovPbP(lqwG~PD`K-&~d~lIG1e}4~PZ@ zP!2J(L?j%Eq_C+Gq#ze7qI%>VOigjeL}XL2kTcLOAi_y;8em>aOkOGT(GF}69XK#| z;6QN4z-dfSHUzHizWeU(YXcjm;|C5595|5JtQXG>WFrIf^8=CWz`5^#|9r#K7cM;A z5bi04=jX$1-MmqRG0bn(w%B2AL-?S=%k0ww=b0fI#O|*eLxzbnhu;c-rF!Gd#AK!lurpEU!!ux7X(8$Ac_pJ>^P2FRhrd5}j# zpMw2A1~>va-yp()5_!Y=YsvDjW=N+q(72{ktyPx^3&Ws)^^sCcHm~ z09_Qb`dAn!pw)mqRfj1Yd@6CAG@WAB-KRGq?-_Br@IGd-3{P0)QN^H_`I7^|#4L}x z3V_=PH!CaZk;;g;UXA>fWzTfe$?e-uZvXuU4{P_8Z@LoRr6Ju*P~{u}ntRjsrnZsM zw&vkCkEZ)V3H?M3$gr(^E2m0py0dnP%L&Rx zCAE3HR*{7K6-WQs-F zO~%|MKBOhA1JoNsKepc33vy;ogr8W`YOIg$_u6(4uGgI z35@1P(#@zRZaWAow z$dSV4&Jw#NQx8JL;r-4I9?TZ?@iXSQ~;jkK4$a!${Yt?!=d z$N5e#otZtJ$@Fh?5BHg7->`dIeeV3LLNHmn=s;s~!H)^1+L#oM@^t z2|vphd%7u=^m*)tn~7(tIUYbgWinASED7vA9vc>%qHT}#jV%ChH73Pv!jM&G>%al`@?Yv!_kn>DOgita>NDt9C9{a>6I~r1Urzz2r%gFu1u(4o zFto)0dem!b094f4`z7=|rJvV1tMm?Q9F{Wv;!i*RgR4X1KUF?D3V-K*tNh67w$^iG zgro~u8RWi8FE6RSYJPsJ>DEd4G5%70MJ(s}`VoUr%3xAyXXlTDjI{-CNEt)Tq8M+gz38DXsb}vR#4}xNm*m zw2oJ=U*Nc!wwg!P@+`5fk<}iH9OsnP2I{=^nJWET)M`zi~T2S*X7t>gy&^b zt4H;|OSQZUwXI2pMfTa$?Xc*u zw%V~--BMq9w$(iKyt0+_g;ycYtvsjX;rJq2s2$I*Ntb*I;+)kNlq<%D$>@)xyPFE1f**e~>QcmMqdbSKFUwy9h0;*~)&M)}A|0dY$C_(wcl` z?U=3APM4lv8=hqzP*f(p( zi)`yOU)%pkZM(o$`hbv}TY^kT(q)d3G&w#Gt(NOAFkz*yo)u;u@zdV zk;ztQeob2a+!AR*a!%+>jTGCpw7{{@(j~OWvCyUI3#19v(=Rzza=cihwdc+$rOY+w zBv1W(j(wqp8p*zpStUcU{aZ-B0inejHL#U)b*iVICk;}}bwc%WtaoE_yjY_Jwrfew z3DwgS`$7xwuR=@g3z?TtJ-xP7=amv#sTYSU_gt*)FR>M>AD^!s=hzCJuh9ZqAq6eg zXsMPa>9Q|m)=1KXBu%J(Y_h+WI9$mi`RcS#yQZFZp63=?BweW9XP0W%)YI$7Insp| zYGkrqOOJCbM4DQFma4Q;K5156&eyh_U#Z*LeaRGTB1pwbI$t{}_p7G`2{3R1SKPoG|~!gV4`lgH7l$ZWD&~L1!^+EkcK(rK})m0~CUL4fa6KD>cNm zWv(0Md50-~l>3ZwZ6ybt2Lq1!tY1=U1KaU)N^K-<(-=hkHgW%n(-3LqBT7xqK%`AA zKtEAxGxypur_@$A^f{%r*&)u6P0diZ?HunIfVkhzmD)e9)B*Ax>{05fCzLugrqtoPlsfWFrH(brrM#QHKY(?*q>%^&oZoRjzsS0j1vJhd6%dtWs~C zh8|PuZDFO}PTRcwDWx8MP^ou}L(eMp&J1)MBK@5glzJECzKe3t3`1P^?g5B%@1f4` z=eghiU8Nr7_yb(?80SCqHKjhhsMJTuw`eN$8_=RU8L`U2&D@q|)OQ|6aA|0VAGW!mA( zFDdnvb4oq4PpRJ}{db>H>i6T&PZ%^P|Eo_b^#_#uwMUft#uG~Yv0te_8B^+;(6>nc zb`NxxRroOUU8Vl>=al-hX{G+0YrjKz-xaz`sqa0d)N?N?b)NFhKd;meO{M;pYyb9M zrT%UP;(7kZgG&7*3{i)_KL9f(e-1#$mHJ;fh~s~u9{>8NQa|JPXD=!B^I4^qIlufIgZ@)k*S?6a(X&ds zIrcmVaqPVpoRdeD_CKU_U{UGd^C%*J1cOGN2>a1FrDMmHj-P_gE1g(UI(bIv)HjuG zJfL*bV@fkF=*&5#o4KatYf9%bN*8Wdy2x?c38mXRlrA+uq;(8H)TNU$JHM-R7x}y0 z5M^~QK$O#cLFpb7nunf&s7G%R;+}n^_mg*k^g*TckR2j_nev8vAf9Pt7UKNq9P|yP zD~FW^Sxc`Y&$=Hez5Wr1d>hUyJ-$!rjnr@B?MiR*L*$#FJ`H)$x}G! z6d}?!FDN}d06oUSkTSRBAo9)djN4Bry(12B-<{NR=hu{;{T#>^TzeU1Uq<>auG?)w z&nP{2Sm`|vD}6b2z5F>GP=+DS?+ZisLZn^s6blvVwEsb+ugpNVL*G^U0M{KP|3TUQ z4B@I@>BAf!epcxthv2QAU4dq?)qSDtMR{Hq7($@_u zeLZE}z`f~)`o<-tPhNmg`WyyO(@Nj+l+vfqDt#+;zx8RQUr)KWFDQKndG7Q>Tz@BJ z-ATE3lK0MMmA-2ndQ#~(#G!kkCzQVDoYMDE{(V1T!sPs$c$Q!7QTokaQ~IGCMEYAP z37a6{Vw*;3_vd_{cg&6&y3Q)#^Q~JFf5cPRK*F4I#zrLjO z2NsonEUfg0GAwTG(C3u?2UtqFc6%3oU$LM4!>za&nW#w1N4~EA5{?b`{;Ru zK!=t7?bAv>SycLWxaMPQKlUS~KmHI4%AYCyNy_>ZbvR4?J_~(rN$D>fSNdtrf9YPO zzdWz>SGevgPbmG&g3`ZxKzN|9_-??K!3Y z{~Sa;zg|@O5B*AigZzK=HKqUf8%qDlDWw^!^f#YW`ddF!`rF*|JDmF-*Zw!k`im!& z{yyjb`?S)3Ntr)*TInA?rSy-(O8*UIKEDs9cuwiRj zO8?{SN?#a&xW^0Z|I;``x&J%|k?*HPrT^E0(l4D;`d`D)i%S3R3rhct`~95y{rp*_ zmnrMzeK18c%FtubPn2OCKo4^qb?lSMa6F+5=j{qK0caNbnljw@$+#aTWIbEY^UCmY zy^nn#X@1xxKidFu(7?PhLZ_7xrrb!6GNQB4L(u1x5$9Zz^d$Q!6xEHCn;uX`_C;kB z$kRp{?d0irQW>4kD5HzCuIH7(SY-5^QbsT3_W7Z4=rHt@GWt!3yaRL4HpfZMX z$|#es%<=Fe${69q4pd9mQWlSolLm5+) zz4>0~C1p&Lf6KHowk|1SCa#R_T(h0?J9w5I+;_)0Wz3#c#xCkLNB%wY(2taH`2d>Q z+m*3zRvGXn#(ws%d`=k$o>0cYN0o6EsK_(6h?8iTpQxP8p{t<8{>kmH}nl%DLO8q34xx z$8lx+%AzvvB>m1OlyTQ1%6P*!lyMK|?|n=e|7i@mpo}-3QO3dy#5MPyR>lKi=sc`3 z*Sz_#GTuU-w{q>nIc2)ySjjQ2dLj9(j2#v^wrLIAQkBXJ`e{~-)IrVq7eH-7$%e8$$ z4Zpdz?@&4X`m6b!q(7rJgKTmgzGJuZdtBwzHR=ugPO^WD=gO%m&L89G6zAp4otU8B zu1>JGRo%jK-)?Ph!;=61RCXTlaTH4$->R~lu;CpJU_=@$T`oNWOB|qXI{=Z=f_p=d@|wg-rdQd?&TID zq0h=fyH_%8=|ros#{R9eZ>A>Ozs0EQ)3|O+VzrgFSPOktZ+rV|dR1S~n!es(;SoV+ zZ_lzIx2mW2;Jzb*!VUo=Y_W$sE?Ki?<&?>j7hCCKU)r*Iam(_) zHP*_^n$Xh3qFTDz3RWy%x@g66%gwHhaA{(#jVm#`uXpKMOaI*3hW_G z{vn5<{SIxo8QV^LwTDxJ0o`Cqpv{@EIZS7)*4LZ+Qup?-?ya&(Sz7CPVD0*r{dD?& zfh`OAR;^yTVtJ5GXIkunGAsEvtyzSy{ck_^jn23@_toG09ox2Vu0+ zT1*Gf5?V?J(m`}EEu-bMf>zQYw2D^K8d^(-(qVKs9YIIZQFJsNL*Jxh?P`VN=>$5F zPNI|P6grhoqtmIM&Y&~tEIOOcp>ydxI-f3}3+W=dm@c79=`y;UuApzxm2?$dP1n%1 zbRAt!H_(lA6WvU=(5-YE-A;GVopcx7O%YXT9o<9s(tUJ4JwWU2xf&1A!}JI}N{`Xw z_O0cU^b|c!&(O2<96e7j(2MjEy-csrtMnSZPH)hg^cKBM@6fyS9=%T=(1-L9eN3Ow zr}PQ z`YZj7{!ag(f6~9`bGrh^uD+;UIcb;u*gJger4#-NO?xZvNZ!~kp|O|D@uqe~=4c+n zV|g>)oX7DNT+dtbR=hQD!+~84ye%hrJU8+L-i{~o_B@H3IK|D}!jn1884lU8ax1rS zJ7>9rr|=Fum3QQ6yc197PR?} zi}+%`gfHdG_;S9&?#;Q9ui~ru8orjV zd-z_ykMHLPcs)PJ5AnnN2tUe?@#FjiKgmz=)BFrS%g^!i`~ttoFY(L#3ct#)@$38s zzsYa$+x!l{%kS~~`~iQ+AMwZh34h9;@wfRq{9XPYf1iKAKja_rkNGG3Q~nwMoPWW; zgE!T;oc@#pru`9`j z$#B_7M#xCnST+&6HcK{@Q8HS_$XMA-HkWa-h1AQIvXyKt+ejb{vaKX#yfn%L*-j?P z_A*JDBqhz#B9kR8841OSN~^Re!g$dPiC94*JlH|1D4PL7uo)5TAq<- z{K9CRPBl%c9kx%8bA#0Z}EoIVa zq#2|kQioKLwjymq+Kx1fv;%1;(j3w}(gM;V(o&pepr3($2KpK3XP}>feg^s(=x3mx zfqn-18R%!ApMib``Wfhl&<~*>LO+Cl2>lTHA@uF3AGP@lp&vp&gnkJ95c(nXL+FRl zchGmxchGmxchGmxx686>x(@mdx(>PyIu1GxItm?yjzUMFqtH>bs~DG}eMS3$xF2CU?nju8`w^z&@rLPmykR=-SD23b6{chT zFdgfM=~zEZ$NFJ9)(_LMewcu7WyoHFj5b+iw z-a^D%hM7-HU0^)fM7)KFw-E6bBHlv8 zTZnkGXIR9#h_?{&79!q4#9N4X3lVRj!~7%eLd0F@F#itok2nkwhauuHL>z{QL%YKy z?g!#9L>z{SEr{WT`;;$9@ ztgLB9?9?a*(BemnH-SrFb=?4~57&~Jx+JM`P3-wyqD=(j_^9s2Fi??5{p zXr}}1bfBFMw9|ohI?zrB+UXc*2jl3#I65(oPK=`yW!9!k(FL9aBhU##cg{|^4|;PVbX@8I(ezV6`P4*u=n-wyum;NK4Z?cm=I{_Wu3 z4*u=n-wwXz;9Cy9<=|TmzUAOs4!&h?d9R&E4!-5!TMoYE;9Cy9<=|TmzUAOs4!-5! zTMoYE;9GVTeyjuEa_}t&-*WIR2j6n=EeGFn@GS@5a_}t&-*WIR2j6n=ExSTI)`f35 z_?CljIrx@?Z#npugKs(bmV<9O_?CljIrx@?Z#npugKs(bmOVBv?gxC!*|WsFk2(03 zgKs(bmV<9O_?CljIrx@?Z@KpPJal&VW39bx9M3KrpGPi>@n@l*jpN*9p`XR^&&F}? zvT>ZdY<#}BYNb{MNy59sJh8 zZyo&B!EYV>)^$K1e(T`34u0$4w+?>m;I|Hb>)^Kze(T`34u0$4w+?>m;I|Hb>)^Kz ze(T`34u0$4w+?>m;I|Hb>)^Kze(T`34u0$4w=ReI&td*^n1A@NgAY6Su!9df_^^Wy zJNU4J54${$Km6F`as1)S4!-R2(9c6Z5B)smKab;I!10HVJNUSRk30CdDShpzFEsAxEf@cbzDR`z>wsAPqA)MtXmZ87R9WQLI}O>lVejMX_#CtXu51$awr%x7h6^*p83CV%?%xwm)-4MD+Y9+(J@8+_e+B;){8#W_!G8t+75rE5U%`I`{}udK@L$1y1^*TN zSMXoKe+B;){8#W_!GC+iK-_QeU%`I`{}udK@L$1y1^*TNSMXoKe|suw+%EWU&oRJu zeEuu=Z_i&rd3^pW_^;re?{@V*ie?{ww&e;Je?{ww&e;Je?{ww&e;Je?{ww&e;Je?{ww&e z;Je?{ww&e;Je?{ww&e;Je? z{ww&e;JAR1om>Gki}%e9cdj|Av(02K8AOR>t|vNfp@~I< zCN)QcQ-SXh7Z*iTT-aTxbz(?dm}p8?c~E0_Q0!0k_(`jcF;Va5AqwhEZ9p!P#$-<+ z-g#)MT31($sHe~z4NF;jK@bhg@8YMxGRa&e8t%8|+SYK}YL14dg7t$E3;nM}CKPRy z?+*IAgJ>h`Z*#O!YS#R6bqEzIDp$#7#tucU-{#e1hfQio+HM2+`uKCd z3pdt2t=KSTSm$SS+x&eoWmUV)^<@&p;BkP)?WlieDf) z>R_LaO;v~ZbhA`-s82UfRfqX>Tq+vc^ncXe!nDWPKJ}*U(=AQgr(2n}Pq#L0pKfE? zJ`GIUrwyj<(``-Lr%BWH>G)JIwH7~(DI4<0?jUay+U*ZOkEPN0Ne#`>gjCen6g67p zZD&C?!(w9~^pZWE#DHXKVdef9j zL=wlhoh1W$KAW)1wfs+ES~6RmL}RR1DI0_JV4zmF1yE16IciR|Y&NwyYWZ(IwIjdC zYD~7tON?y{T7nt=9J4{s?C+nEoMC59d7+IVB0HB`?9m&eO(mT&ZHqnouT~`*Tx{y= zZ%GEh)P8F#^To$OOWZ~@$QwnmDTuoLc{F`a`5q1=sK19N$QG5HKeLCLLztc^CQBCB zd5cmzixN$-O>w@vHyKI3r`Jwi&iB+?zPn;)f>rA=VY9&*JD*0YpvUFGr#&|L*r->o3dQ`uw03(Y*eC{OzAcpuTKw=!=1)^AE!7D$+9JDsL$OW%7sc5P#kJZ4yMCok zM=DA;4Se#IMyI5rOjExda(`_5E%^V7uuXPLG{N+DsLhCvmw^e4EEbX$sx20|aUZAJ z*=TOIA+QF9_`gTajQ?M-_}TI2>eOVmzTry;uAzdS?C6I)Fm==XC>#9z`i`Kt8wR$M z4Qxz&#;i;DlVem%l(G|K`u|YgX+A)sM@9Cg*C>~Y?3J%k-p@_ZW-ln2jStL5!HmJq zn7mD1F}2Qa-mpx`GUPKeQtPNzva@AsB{O}U9qprCd>x;e-fwZUuOr)%=LAAW}dI(GxL2NpIPAROtZ`$zK+lA>FfAR+1K%zimx-p zGJE+tKC`#4<1_pCIzF>+DhfAD^L~CKYO~_~Yq@sIb=RWCinF%SlZxDi+6(st43^zUq=%&R1QUidr{RJ}Pu$fW9EDpbeQ oX>-ca^wY}K!BVg|vn~63!Ef4M2KKX_K0i4cbXK)4QAi~I6Dvx+UjP6A literal 0 HcmV?d00001 diff --git a/app/www/lib/ionic/fonts/ionicons.woff b/app/www/lib/ionic/fonts/ionicons.woff new file mode 100644 index 0000000000000000000000000000000000000000..5f3a14e0a5ca6d20cc4fac708979e807b0d51bc3 GIT binary patch literal 67904 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

  • F(S(gioucm()OHA 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 E0LEz`i~s-t literal 0 HcmV?d00001 diff --git a/app/www/lib/ionic/js/angular-ui/angular-ui-router.js b/app/www/lib/ionic/js/angular-ui/angular-ui-router.js new file mode 100644 index 0000000..d2636f8 --- /dev/null +++ b/app/www/lib/ionic/js/angular-ui/angular-ui-router.js @@ -0,0 +1,4232 @@ +/** + * State-based routing for AngularJS + * @version v0.2.13 + * @link http://angular-ui.github.com/ + * @license MIT License, http://www.opensource.org/licenses/MIT + */ + +/* commonjs package manager support (eg componentjs) */ +if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){ + module.exports = 'ui.router'; +} + +(function (window, angular, undefined) { +/*jshint globalstrict:true*/ +/*global angular:false*/ +'use strict'; + +var isDefined = angular.isDefined, + isFunction = angular.isFunction, + isString = angular.isString, + isObject = angular.isObject, + isArray = angular.isArray, + forEach = angular.forEach, + extend = angular.extend, + copy = angular.copy; + +function inherit(parent, extra) { + return extend(new (extend(function() {}, { prototype: parent }))(), extra); +} + +function merge(dst) { + forEach(arguments, function(obj) { + if (obj !== dst) { + forEach(obj, function(value, key) { + if (!dst.hasOwnProperty(key)) dst[key] = value; + }); + } + }); + return dst; +} + +/** + * Finds the common ancestor path between two states. + * + * @param {Object} first The first state. + * @param {Object} second The second state. + * @return {Array} Returns an array of state names in descending order, not including the root. + */ +function ancestors(first, second) { + var path = []; + + for (var n in first.path) { + if (first.path[n] !== second.path[n]) break; + path.push(first.path[n]); + } + return path; +} + +/** + * IE8-safe wrapper for `Object.keys()`. + * + * @param {Object} object A JavaScript object. + * @return {Array} Returns the keys of the object as an array. + */ +function objectKeys(object) { + if (Object.keys) { + return Object.keys(object); + } + var result = []; + + angular.forEach(object, function(val, key) { + result.push(key); + }); + return result; +} + +/** + * IE8-safe wrapper for `Array.prototype.indexOf()`. + * + * @param {Array} array A JavaScript array. + * @param {*} value A value to search the array for. + * @return {Number} Returns the array index value of `value`, or `-1` if not present. + */ +function indexOf(array, value) { + if (Array.prototype.indexOf) { + return array.indexOf(value, Number(arguments[2]) || 0); + } + var len = array.length >>> 0, from = Number(arguments[2]) || 0; + from = (from < 0) ? Math.ceil(from) : Math.floor(from); + + if (from < 0) from += len; + + for (; from < len; from++) { + if (from in array && array[from] === value) return from; + } + return -1; +} + +/** + * Merges a set of parameters with all parameters inherited between the common parents of the + * current state and a given destination state. + * + * @param {Object} currentParams The value of the current state parameters ($stateParams). + * @param {Object} newParams The set of parameters which will be composited with inherited params. + * @param {Object} $current Internal definition of object representing the current state. + * @param {Object} $to Internal definition of object representing state to transition to. + */ +function inheritParams(currentParams, newParams, $current, $to) { + var parents = ancestors($current, $to), parentParams, inherited = {}, inheritList = []; + + for (var i in parents) { + if (!parents[i].params) continue; + parentParams = objectKeys(parents[i].params); + if (!parentParams.length) continue; + + for (var j in parentParams) { + if (indexOf(inheritList, parentParams[j]) >= 0) continue; + inheritList.push(parentParams[j]); + inherited[parentParams[j]] = currentParams[parentParams[j]]; + } + } + return extend({}, inherited, newParams); +} + +/** + * Performs a non-strict comparison of the subset of two objects, defined by a list of keys. + * + * @param {Object} a The first object. + * @param {Object} b The second object. + * @param {Array} keys The list of keys within each object to compare. If the list is empty or not specified, + * it defaults to the list of keys in `a`. + * @return {Boolean} Returns `true` if the keys match, otherwise `false`. + */ +function equalForKeys(a, b, keys) { + if (!keys) { + keys = []; + for (var n in a) keys.push(n); // Used instead of Object.keys() for IE8 compatibility + } + + for (var i=0; i + * + * + * + * + * + * + * + * + * + * + * + * + */ +angular.module('ui.router', ['ui.router.state']); + +angular.module('ui.router.compat', ['ui.router']); + +/** + * @ngdoc object + * @name ui.router.util.$resolve + * + * @requires $q + * @requires $injector + * + * @description + * Manages resolution of (acyclic) graphs of promises. + */ +$Resolve.$inject = ['$q', '$injector']; +function $Resolve( $q, $injector) { + + var VISIT_IN_PROGRESS = 1, + VISIT_DONE = 2, + NOTHING = {}, + NO_DEPENDENCIES = [], + NO_LOCALS = NOTHING, + NO_PARENT = extend($q.when(NOTHING), { $$promises: NOTHING, $$values: NOTHING }); + + + /** + * @ngdoc function + * @name ui.router.util.$resolve#study + * @methodOf ui.router.util.$resolve + * + * @description + * Studies a set of invocables that are likely to be used multiple times. + *
    +   * $resolve.study(invocables)(locals, parent, self)
    +   * 
    + * is equivalent to + *
    +   * $resolve.resolve(invocables, locals, parent, self)
    +   * 
    + * but the former is more efficient (in fact `resolve` just calls `study` + * internally). + * + * @param {object} invocables Invocable objects + * @return {function} a function to pass in locals, parent and self + */ + this.study = function (invocables) { + if (!isObject(invocables)) throw new Error("'invocables' must be an object"); + var invocableKeys = objectKeys(invocables || {}); + + // Perform a topological sort of invocables to build an ordered plan + var plan = [], cycle = [], visited = {}; + function visit(value, key) { + if (visited[key] === VISIT_DONE) return; + + cycle.push(key); + if (visited[key] === VISIT_IN_PROGRESS) { + cycle.splice(0, indexOf(cycle, key)); + throw new Error("Cyclic dependency: " + cycle.join(" -> ")); + } + visited[key] = VISIT_IN_PROGRESS; + + if (isString(value)) { + plan.push(key, [ function() { return $injector.get(value); }], NO_DEPENDENCIES); + } else { + var params = $injector.annotate(value); + forEach(params, function (param) { + if (param !== key && invocables.hasOwnProperty(param)) visit(invocables[param], param); + }); + plan.push(key, value, params); + } + + cycle.pop(); + visited[key] = VISIT_DONE; + } + forEach(invocables, visit); + invocables = cycle = visited = null; // plan is all that's required + + function isResolve(value) { + return isObject(value) && value.then && value.$$promises; + } + + return function (locals, parent, self) { + if (isResolve(locals) && self === undefined) { + self = parent; parent = locals; locals = null; + } + if (!locals) locals = NO_LOCALS; + else if (!isObject(locals)) { + throw new Error("'locals' must be an object"); + } + if (!parent) parent = NO_PARENT; + else if (!isResolve(parent)) { + throw new Error("'parent' must be a promise returned by $resolve.resolve()"); + } + + // To complete the overall resolution, we have to wait for the parent + // promise and for the promise for each invokable in our plan. + var resolution = $q.defer(), + result = resolution.promise, + promises = result.$$promises = {}, + values = extend({}, locals), + wait = 1 + plan.length/3, + merged = false; + + function done() { + // Merge parent values we haven't got yet and publish our own $$values + if (!--wait) { + if (!merged) merge(values, parent.$$values); + result.$$values = values; + result.$$promises = result.$$promises || true; // keep for isResolve() + delete result.$$inheritedValues; + resolution.resolve(values); + } + } + + function fail(reason) { + result.$$failure = reason; + resolution.reject(reason); + } + + // Short-circuit if parent has already failed + if (isDefined(parent.$$failure)) { + fail(parent.$$failure); + return result; + } + + if (parent.$$inheritedValues) { + merge(values, omit(parent.$$inheritedValues, invocableKeys)); + } + + // Merge parent values if the parent has already resolved, or merge + // parent promises and wait if the parent resolve is still in progress. + extend(promises, parent.$$promises); + if (parent.$$values) { + merged = merge(values, omit(parent.$$values, invocableKeys)); + result.$$inheritedValues = omit(parent.$$values, invocableKeys); + done(); + } else { + if (parent.$$inheritedValues) { + result.$$inheritedValues = omit(parent.$$inheritedValues, invocableKeys); + } + parent.then(done, fail); + } + + // Process each invocable in the plan, but ignore any where a local of the same name exists. + for (var i=0, ii=plan.length; i} The template html as a string, or a promise + * for that string. + */ + this.fromUrl = function (url, params) { + if (isFunction(url)) url = url(params); + if (url == null) return null; + else return $http + .get(url, { cache: $templateCache, headers: { Accept: 'text/html' }}) + .then(function(response) { return response.data; }); + }; + + /** + * @ngdoc function + * @name ui.router.util.$templateFactory#fromProvider + * @methodOf ui.router.util.$templateFactory + * + * @description + * Creates a template by invoking an injectable provider function. + * + * @param {Function} provider Function to invoke via `$injector.invoke` + * @param {Object} params Parameters for the template. + * @param {Object} locals Locals to pass to `invoke`. Defaults to + * `{ params: params }`. + * @return {string|Promise.} The template html as a string, or a promise + * for that string. + */ + this.fromProvider = function (provider, params, locals) { + return $injector.invoke(provider, null, locals || { params: params }); + }; +} + +angular.module('ui.router.util').service('$templateFactory', $TemplateFactory); + +var $$UMFP; // reference to $UrlMatcherFactoryProvider + +/** + * @ngdoc object + * @name ui.router.util.type:UrlMatcher + * + * @description + * Matches URLs against patterns and extracts named parameters from the path or the search + * part of the URL. A URL pattern consists of a path pattern, optionally followed by '?' and a list + * of search parameters. Multiple search parameter names are separated by '&'. Search parameters + * do not influence whether or not a URL is matched, but their values are passed through into + * the matched parameters returned by {@link ui.router.util.type:UrlMatcher#methods_exec exec}. + * + * Path parameter placeholders can be specified using simple colon/catch-all syntax or curly brace + * syntax, which optionally allows a regular expression for the parameter to be specified: + * + * * `':'` name - colon placeholder + * * `'*'` name - catch-all placeholder + * * `'{' name '}'` - curly placeholder + * * `'{' name ':' regexp|type '}'` - curly placeholder with regexp or type name. Should the + * regexp itself contain curly braces, they must be in matched pairs or escaped with a backslash. + * + * Parameter names may contain only word characters (latin letters, digits, and underscore) and + * must be unique within the pattern (across both path and search parameters). For colon + * placeholders or curly placeholders without an explicit regexp, a path parameter matches any + * number of characters other than '/'. For catch-all placeholders the path parameter matches + * any number of characters. + * + * Examples: + * + * * `'/hello/'` - Matches only if the path is exactly '/hello/'. There is no special treatment for + * trailing slashes, and patterns have to match the entire path, not just a prefix. + * * `'/user/:id'` - Matches '/user/bob' or '/user/1234!!!' or even '/user/' but not '/user' or + * '/user/bob/details'. The second path segment will be captured as the parameter 'id'. + * * `'/user/{id}'` - Same as the previous example, but using curly brace syntax. + * * `'/user/{id:[^/]*}'` - Same as the previous example. + * * `'/user/{id:[0-9a-fA-F]{1,8}}'` - Similar to the previous example, but only matches if the id + * parameter consists of 1 to 8 hex digits. + * * `'/files/{path:.*}'` - Matches any URL starting with '/files/' and captures the rest of the + * path into the parameter 'path'. + * * `'/files/*path'` - ditto. + * * `'/calendar/{start:date}'` - Matches "/calendar/2014-11-12" (because the pattern defined + * in the built-in `date` Type matches `2014-11-12`) and provides a Date object in $stateParams.start + * + * @param {string} pattern The pattern to compile into a matcher. + * @param {Object} config A configuration object hash: + * @param {Object=} parentMatcher Used to concatenate the pattern/config onto + * an existing UrlMatcher + * + * * `caseInsensitive` - `true` if URL matching should be case insensitive, otherwise `false`, the default value (for backward compatibility) is `false`. + * * `strict` - `false` if matching against a URL with a trailing slash should be treated as equivalent to a URL without a trailing slash, the default value is `true`. + * + * @property {string} prefix A static prefix of this pattern. The matcher guarantees that any + * URL matching this matcher (i.e. any string for which {@link ui.router.util.type:UrlMatcher#methods_exec exec()} returns + * non-null) will start with this prefix. + * + * @property {string} source The pattern that was passed into the constructor + * + * @property {string} sourcePath The path portion of the source property + * + * @property {string} sourceSearch The search portion of the source property + * + * @property {string} regex The constructed regex that will be used to match against the url when + * it is time to determine which url will match. + * + * @returns {Object} New `UrlMatcher` object + */ +function UrlMatcher(pattern, config, parentMatcher) { + config = extend({ params: {} }, isObject(config) ? config : {}); + + // Find all placeholders and create a compiled pattern, using either classic or curly syntax: + // '*' name + // ':' name + // '{' name '}' + // '{' name ':' regexp '}' + // The regular expression is somewhat complicated due to the need to allow curly braces + // inside the regular expression. The placeholder regexp breaks down as follows: + // ([:*])([\w\[\]]+) - classic placeholder ($1 / $2) (search version has - for snake-case) + // \{([\w\[\]]+)(?:\:( ... ))?\} - curly brace placeholder ($3) with optional regexp/type ... ($4) (search version has - for snake-case + // (?: ... | ... | ... )+ - the regexp consists of any number of atoms, an atom being either + // [^{}\\]+ - anything other than curly braces or backslash + // \\. - a backslash escape + // \{(?:[^{}\\]+|\\.)*\} - a matched set of curly braces containing other atoms + var placeholder = /([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g, + searchPlaceholder = /([:]?)([\w\[\]-]+)|\{([\w\[\]-]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g, + compiled = '^', last = 0, m, + segments = this.segments = [], + parentParams = parentMatcher ? parentMatcher.params : {}, + params = this.params = parentMatcher ? parentMatcher.params.$$new() : new $$UMFP.ParamSet(), + paramNames = []; + + function addParameter(id, type, config, location) { + paramNames.push(id); + if (parentParams[id]) return parentParams[id]; + if (!/^\w+(-+\w+)*(?:\[\])?$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'"); + if (params[id]) throw new Error("Duplicate parameter name '" + id + "' in pattern '" + pattern + "'"); + params[id] = new $$UMFP.Param(id, type, config, location); + return params[id]; + } + + function quoteRegExp(string, pattern, squash) { + var surroundPattern = ['',''], result = string.replace(/[\\\[\]\^$*+?.()|{}]/g, "\\$&"); + if (!pattern) return result; + switch(squash) { + case false: surroundPattern = ['(', ')']; break; + case true: surroundPattern = ['?(', ')?']; break; + default: surroundPattern = ['(' + squash + "|", ')?']; break; + } + return result + surroundPattern[0] + pattern + surroundPattern[1]; + } + + this.source = pattern; + + // Split into static segments separated by path parameter placeholders. + // The number of segments is always 1 more than the number of parameters. + function matchDetails(m, isSearch) { + var id, regexp, segment, type, cfg, arrayMode; + id = m[2] || m[3]; // IE[78] returns '' for unmatched groups instead of null + cfg = config.params[id]; + segment = pattern.substring(last, m.index); + regexp = isSearch ? m[4] : m[4] || (m[1] == '*' ? '.*' : null); + type = $$UMFP.type(regexp || "string") || inherit($$UMFP.type("string"), { pattern: new RegExp(regexp) }); + return { + id: id, regexp: regexp, segment: segment, type: type, cfg: cfg + }; + } + + var p, param, segment; + while ((m = placeholder.exec(pattern))) { + p = matchDetails(m, false); + if (p.segment.indexOf('?') >= 0) break; // we're into the search part + + param = addParameter(p.id, p.type, p.cfg, "path"); + compiled += quoteRegExp(p.segment, param.type.pattern.source, param.squash); + segments.push(p.segment); + last = placeholder.lastIndex; + } + segment = pattern.substring(last); + + // Find any search parameter names and remove them from the last segment + var i = segment.indexOf('?'); + + if (i >= 0) { + var search = this.sourceSearch = segment.substring(i); + segment = segment.substring(0, i); + this.sourcePath = pattern.substring(0, last + i); + + if (search.length > 0) { + last = 0; + while ((m = searchPlaceholder.exec(search))) { + p = matchDetails(m, true); + param = addParameter(p.id, p.type, p.cfg, "search"); + last = placeholder.lastIndex; + // check if ?& + } + } + } else { + this.sourcePath = pattern; + this.sourceSearch = ''; + } + + compiled += quoteRegExp(segment) + (config.strict === false ? '\/?' : '') + '$'; + segments.push(segment); + + this.regexp = new RegExp(compiled, config.caseInsensitive ? 'i' : undefined); + this.prefix = segments[0]; + this.$$paramNames = paramNames; +} + +/** + * @ngdoc function + * @name ui.router.util.type:UrlMatcher#concat + * @methodOf ui.router.util.type:UrlMatcher + * + * @description + * Returns a new matcher for a pattern constructed by appending the path part and adding the + * search parameters of the specified pattern to this pattern. The current pattern is not + * modified. This can be understood as creating a pattern for URLs that are relative to (or + * suffixes of) the current pattern. + * + * @example + * The following two matchers are equivalent: + *
    + * new UrlMatcher('/user/{id}?q').concat('/details?date');
    + * new UrlMatcher('/user/{id}/details?q&date');
    + * 
    + * + * @param {string} pattern The pattern to append. + * @param {Object} config An object hash of the configuration for the matcher. + * @returns {UrlMatcher} A matcher for the concatenated pattern. + */ +UrlMatcher.prototype.concat = function (pattern, config) { + // Because order of search parameters is irrelevant, we can add our own search + // parameters to the end of the new pattern. Parse the new pattern by itself + // and then join the bits together, but it's much easier to do this on a string level. + var defaultConfig = { + caseInsensitive: $$UMFP.caseInsensitive(), + strict: $$UMFP.strictMode(), + squash: $$UMFP.defaultSquashPolicy() + }; + return new UrlMatcher(this.sourcePath + pattern + this.sourceSearch, extend(defaultConfig, config), this); +}; + +UrlMatcher.prototype.toString = function () { + return this.source; +}; + +/** + * @ngdoc function + * @name ui.router.util.type:UrlMatcher#exec + * @methodOf ui.router.util.type:UrlMatcher + * + * @description + * Tests the specified path against this matcher, and returns an object containing the captured + * parameter values, or null if the path does not match. The returned object contains the values + * of any search parameters that are mentioned in the pattern, but their value may be null if + * they are not present in `searchParams`. This means that search parameters are always treated + * as optional. + * + * @example + *
    + * new UrlMatcher('/user/{id}?q&r').exec('/user/bob', {
    + *   x: '1', q: 'hello'
    + * });
    + * // returns { id: 'bob', q: 'hello', r: null }
    + * 
    + * + * @param {string} path The URL path to match, e.g. `$location.path()`. + * @param {Object} searchParams URL search parameters, e.g. `$location.search()`. + * @returns {Object} The captured parameter values. + */ +UrlMatcher.prototype.exec = function (path, searchParams) { + var m = this.regexp.exec(path); + if (!m) return null; + searchParams = searchParams || {}; + + var paramNames = this.parameters(), nTotal = paramNames.length, + nPath = this.segments.length - 1, + values = {}, i, j, cfg, paramName; + + if (nPath !== m.length - 1) throw new Error("Unbalanced capture group in route '" + this.source + "'"); + + function decodePathArray(string) { + function reverseString(str) { return str.split("").reverse().join(""); } + function unquoteDashes(str) { return str.replace(/\\-/, "-"); } + + var split = reverseString(string).split(/-(?!\\)/); + var allReversed = map(split, reverseString); + return map(allReversed, unquoteDashes).reverse(); + } + + for (i = 0; i < nPath; i++) { + paramName = paramNames[i]; + var param = this.params[paramName]; + var paramVal = m[i+1]; + // if the param value matches a pre-replace pair, replace the value before decoding. + for (j = 0; j < param.replace; j++) { + if (param.replace[j].from === paramVal) paramVal = param.replace[j].to; + } + if (paramVal && param.array === true) paramVal = decodePathArray(paramVal); + values[paramName] = param.value(paramVal); + } + for (/**/; i < nTotal; i++) { + paramName = paramNames[i]; + values[paramName] = this.params[paramName].value(searchParams[paramName]); + } + + return values; +}; + +/** + * @ngdoc function + * @name ui.router.util.type:UrlMatcher#parameters + * @methodOf ui.router.util.type:UrlMatcher + * + * @description + * Returns the names of all path and search parameters of this pattern in an unspecified order. + * + * @returns {Array.} An array of parameter names. Must be treated as read-only. If the + * pattern has no parameters, an empty array is returned. + */ +UrlMatcher.prototype.parameters = function (param) { + if (!isDefined(param)) return this.$$paramNames; + return this.params[param] || null; +}; + +/** + * @ngdoc function + * @name ui.router.util.type:UrlMatcher#validate + * @methodOf ui.router.util.type:UrlMatcher + * + * @description + * Checks an object hash of parameters to validate their correctness according to the parameter + * types of this `UrlMatcher`. + * + * @param {Object} params The object hash of parameters to validate. + * @returns {boolean} Returns `true` if `params` validates, otherwise `false`. + */ +UrlMatcher.prototype.validates = function (params) { + return this.params.$$validates(params); +}; + +/** + * @ngdoc function + * @name ui.router.util.type:UrlMatcher#format + * @methodOf ui.router.util.type:UrlMatcher + * + * @description + * Creates a URL that matches this pattern by substituting the specified values + * for the path and search parameters. Null values for path parameters are + * treated as empty strings. + * + * @example + *
    + * new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' });
    + * // returns '/user/bob?q=yes'
    + * 
    + * + * @param {Object} values the values to substitute for the parameters in this pattern. + * @returns {string} the formatted URL (path and optionally search part). + */ +UrlMatcher.prototype.format = function (values) { + values = values || {}; + var segments = this.segments, params = this.parameters(), paramset = this.params; + if (!this.validates(values)) return null; + + var i, search = false, nPath = segments.length - 1, nTotal = params.length, result = segments[0]; + + function encodeDashes(str) { // Replace dashes with encoded "\-" + return encodeURIComponent(str).replace(/-/g, function(c) { return '%5C%' + c.charCodeAt(0).toString(16).toUpperCase(); }); + } + + for (i = 0; i < nTotal; i++) { + var isPathParam = i < nPath; + var name = params[i], param = paramset[name], value = param.value(values[name]); + var isDefaultValue = param.isOptional && param.type.equals(param.value(), value); + var squash = isDefaultValue ? param.squash : false; + var encoded = param.type.encode(value); + + if (isPathParam) { + var nextSegment = segments[i + 1]; + if (squash === false) { + if (encoded != null) { + if (isArray(encoded)) { + result += map(encoded, encodeDashes).join("-"); + } else { + result += encodeURIComponent(encoded); + } + } + result += nextSegment; + } else if (squash === true) { + var capture = result.match(/\/$/) ? /\/?(.*)/ : /(.*)/; + result += nextSegment.match(capture)[1]; + } else if (isString(squash)) { + result += squash + nextSegment; + } + } else { + if (encoded == null || (isDefaultValue && squash !== false)) continue; + if (!isArray(encoded)) encoded = [ encoded ]; + encoded = map(encoded, encodeURIComponent).join('&' + name + '='); + result += (search ? '&' : '?') + (name + '=' + encoded); + search = true; + } + } + + return result; +}; + +/** + * @ngdoc object + * @name ui.router.util.type:Type + * + * @description + * Implements an interface to define custom parameter types that can be decoded from and encoded to + * string parameters matched in a URL. Used by {@link ui.router.util.type:UrlMatcher `UrlMatcher`} + * objects when matching or formatting URLs, or comparing or validating parameter values. + * + * See {@link ui.router.util.$urlMatcherFactory#methods_type `$urlMatcherFactory#type()`} for more + * information on registering custom types. + * + * @param {Object} config A configuration object which contains the custom type definition. The object's + * properties will override the default methods and/or pattern in `Type`'s public interface. + * @example + *
    + * {
    + *   decode: function(val) { return parseInt(val, 10); },
    + *   encode: function(val) { return val && val.toString(); },
    + *   equals: function(a, b) { return this.is(a) && a === b; },
    + *   is: function(val) { return angular.isNumber(val) isFinite(val) && val % 1 === 0; },
    + *   pattern: /\d+/
    + * }
    + * 
    + * + * @property {RegExp} pattern The regular expression pattern used to match values of this type when + * coming from a substring of a URL. + * + * @returns {Object} Returns a new `Type` object. + */ +function Type(config) { + extend(this, config); +} + +/** + * @ngdoc function + * @name ui.router.util.type:Type#is + * @methodOf ui.router.util.type:Type + * + * @description + * Detects whether a value is of a particular type. Accepts a native (decoded) value + * and determines whether it matches the current `Type` object. + * + * @param {*} val The value to check. + * @param {string} key Optional. If the type check is happening in the context of a specific + * {@link ui.router.util.type:UrlMatcher `UrlMatcher`} object, this is the name of the + * parameter in which `val` is stored. Can be used for meta-programming of `Type` objects. + * @returns {Boolean} Returns `true` if the value matches the type, otherwise `false`. + */ +Type.prototype.is = function(val, key) { + return true; +}; + +/** + * @ngdoc function + * @name ui.router.util.type:Type#encode + * @methodOf ui.router.util.type:Type + * + * @description + * Encodes a custom/native type value to a string that can be embedded in a URL. Note that the + * return value does *not* need to be URL-safe (i.e. passed through `encodeURIComponent()`), it + * only needs to be a representation of `val` that has been coerced to a string. + * + * @param {*} val The value to encode. + * @param {string} key The name of the parameter in which `val` is stored. Can be used for + * meta-programming of `Type` objects. + * @returns {string} Returns a string representation of `val` that can be encoded in a URL. + */ +Type.prototype.encode = function(val, key) { + return val; +}; + +/** + * @ngdoc function + * @name ui.router.util.type:Type#decode + * @methodOf ui.router.util.type:Type + * + * @description + * Converts a parameter value (from URL string or transition param) to a custom/native value. + * + * @param {string} val The URL parameter value to decode. + * @param {string} key The name of the parameter in which `val` is stored. Can be used for + * meta-programming of `Type` objects. + * @returns {*} Returns a custom representation of the URL parameter value. + */ +Type.prototype.decode = function(val, key) { + return val; +}; + +/** + * @ngdoc function + * @name ui.router.util.type:Type#equals + * @methodOf ui.router.util.type:Type + * + * @description + * Determines whether two decoded values are equivalent. + * + * @param {*} a A value to compare against. + * @param {*} b A value to compare against. + * @returns {Boolean} Returns `true` if the values are equivalent/equal, otherwise `false`. + */ +Type.prototype.equals = function(a, b) { + return a == b; +}; + +Type.prototype.$subPattern = function() { + var sub = this.pattern.toString(); + return sub.substr(1, sub.length - 2); +}; + +Type.prototype.pattern = /.*/; + +Type.prototype.toString = function() { return "{Type:" + this.name + "}"; }; + +/* + * Wraps an existing custom Type as an array of Type, depending on 'mode'. + * e.g.: + * - urlmatcher pattern "/path?{queryParam[]:int}" + * - url: "/path?queryParam=1&queryParam=2 + * - $stateParams.queryParam will be [1, 2] + * if `mode` is "auto", then + * - url: "/path?queryParam=1 will create $stateParams.queryParam: 1 + * - url: "/path?queryParam=1&queryParam=2 will create $stateParams.queryParam: [1, 2] + */ +Type.prototype.$asArray = function(mode, isSearch) { + if (!mode) return this; + if (mode === "auto" && !isSearch) throw new Error("'auto' array mode is for query parameters only"); + return new ArrayType(this, mode); + + function ArrayType(type, mode) { + function bindTo(type, callbackName) { + return function() { + return type[callbackName].apply(type, arguments); + }; + } + + // Wrap non-array value as array + function arrayWrap(val) { return isArray(val) ? val : (isDefined(val) ? [ val ] : []); } + // Unwrap array value for "auto" mode. Return undefined for empty array. + function arrayUnwrap(val) { + switch(val.length) { + case 0: return undefined; + case 1: return mode === "auto" ? val[0] : val; + default: return val; + } + } + function falsey(val) { return !val; } + + // Wraps type (.is/.encode/.decode) functions to operate on each value of an array + function arrayHandler(callback, allTruthyMode) { + return function handleArray(val) { + val = arrayWrap(val); + var result = map(val, callback); + if (allTruthyMode === true) + return filter(result, falsey).length === 0; + return arrayUnwrap(result); + }; + } + + // Wraps type (.equals) functions to operate on each value of an array + function arrayEqualsHandler(callback) { + return function handleArray(val1, val2) { + var left = arrayWrap(val1), right = arrayWrap(val2); + if (left.length !== right.length) return false; + for (var i = 0; i < left.length; i++) { + if (!callback(left[i], right[i])) return false; + } + return true; + }; + } + + this.encode = arrayHandler(bindTo(type, 'encode')); + this.decode = arrayHandler(bindTo(type, 'decode')); + this.is = arrayHandler(bindTo(type, 'is'), true); + this.equals = arrayEqualsHandler(bindTo(type, 'equals')); + this.pattern = type.pattern; + this.$arrayMode = mode; + } +}; + + + +/** + * @ngdoc object + * @name ui.router.util.$urlMatcherFactory + * + * @description + * Factory for {@link ui.router.util.type:UrlMatcher `UrlMatcher`} instances. The factory + * is also available to providers under the name `$urlMatcherFactoryProvider`. + */ +function $UrlMatcherFactory() { + $$UMFP = this; + + var isCaseInsensitive = false, isStrictMode = true, defaultSquashPolicy = false; + + function valToString(val) { return val != null ? val.toString().replace(/\//g, "%2F") : val; } + function valFromString(val) { return val != null ? val.toString().replace(/%2F/g, "/") : val; } +// TODO: in 1.0, make string .is() return false if value is undefined by default. +// function regexpMatches(val) { /*jshint validthis:true */ return isDefined(val) && this.pattern.test(val); } + function regexpMatches(val) { /*jshint validthis:true */ return this.pattern.test(val); } + + var $types = {}, enqueue = true, typeQueue = [], injector, defaultTypes = { + string: { + encode: valToString, + decode: valFromString, + is: regexpMatches, + pattern: /[^/]*/ + }, + int: { + encode: valToString, + decode: function(val) { return parseInt(val, 10); }, + is: function(val) { return isDefined(val) && this.decode(val.toString()) === val; }, + pattern: /\d+/ + }, + bool: { + encode: function(val) { return val ? 1 : 0; }, + decode: function(val) { return parseInt(val, 10) !== 0; }, + is: function(val) { return val === true || val === false; }, + pattern: /0|1/ + }, + date: { + encode: function (val) { + if (!this.is(val)) + return undefined; + return [ val.getFullYear(), + ('0' + (val.getMonth() + 1)).slice(-2), + ('0' + val.getDate()).slice(-2) + ].join("-"); + }, + decode: function (val) { + if (this.is(val)) return val; + var match = this.capture.exec(val); + return match ? new Date(match[1], match[2] - 1, match[3]) : undefined; + }, + is: function(val) { return val instanceof Date && !isNaN(val.valueOf()); }, + equals: function (a, b) { return this.is(a) && this.is(b) && a.toISOString() === b.toISOString(); }, + pattern: /[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2][0-9]|3[0-1])/, + capture: /([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/ + }, + json: { + encode: angular.toJson, + decode: angular.fromJson, + is: angular.isObject, + equals: angular.equals, + pattern: /[^/]*/ + }, + any: { // does not encode/decode + encode: angular.identity, + decode: angular.identity, + is: angular.identity, + equals: angular.equals, + pattern: /.*/ + } + }; + + function getDefaultConfig() { + return { + strict: isStrictMode, + caseInsensitive: isCaseInsensitive + }; + } + + function isInjectable(value) { + return (isFunction(value) || (isArray(value) && isFunction(value[value.length - 1]))); + } + + /** + * [Internal] Get the default value of a parameter, which may be an injectable function. + */ + $UrlMatcherFactory.$$getDefaultValue = function(config) { + if (!isInjectable(config.value)) return config.value; + if (!injector) throw new Error("Injectable functions cannot be called at configuration time"); + return injector.invoke(config.value); + }; + + /** + * @ngdoc function + * @name ui.router.util.$urlMatcherFactory#caseInsensitive + * @methodOf ui.router.util.$urlMatcherFactory + * + * @description + * Defines whether URL matching should be case sensitive (the default behavior), or not. + * + * @param {boolean} value `false` to match URL in a case sensitive manner; otherwise `true`; + * @returns {boolean} the current value of caseInsensitive + */ + this.caseInsensitive = function(value) { + if (isDefined(value)) + isCaseInsensitive = value; + return isCaseInsensitive; + }; + + /** + * @ngdoc function + * @name ui.router.util.$urlMatcherFactory#strictMode + * @methodOf ui.router.util.$urlMatcherFactory + * + * @description + * Defines whether URLs should match trailing slashes, or not (the default behavior). + * + * @param {boolean=} value `false` to match trailing slashes in URLs, otherwise `true`. + * @returns {boolean} the current value of strictMode + */ + this.strictMode = function(value) { + if (isDefined(value)) + isStrictMode = value; + return isStrictMode; + }; + + /** + * @ngdoc function + * @name ui.router.util.$urlMatcherFactory#defaultSquashPolicy + * @methodOf ui.router.util.$urlMatcherFactory + * + * @description + * Sets the default behavior when generating or matching URLs with default parameter values. + * + * @param {string} value A string that defines the default parameter URL squashing behavior. + * `nosquash`: When generating an href with a default parameter value, do not squash the parameter value from the URL + * `slash`: When generating an href with a default parameter value, squash (remove) the parameter value, and, if the + * parameter is surrounded by slashes, squash (remove) one slash from the URL + * any other string, e.g. "~": When generating an href with a default parameter value, squash (remove) + * the parameter value from the URL and replace it with this string. + */ + this.defaultSquashPolicy = function(value) { + if (!isDefined(value)) return defaultSquashPolicy; + if (value !== true && value !== false && !isString(value)) + throw new Error("Invalid squash policy: " + value + ". Valid policies: false, true, arbitrary-string"); + defaultSquashPolicy = value; + return value; + }; + + /** + * @ngdoc function + * @name ui.router.util.$urlMatcherFactory#compile + * @methodOf ui.router.util.$urlMatcherFactory + * + * @description + * Creates a {@link ui.router.util.type:UrlMatcher `UrlMatcher`} for the specified pattern. + * + * @param {string} pattern The URL pattern. + * @param {Object} config The config object hash. + * @returns {UrlMatcher} The UrlMatcher. + */ + this.compile = function (pattern, config) { + return new UrlMatcher(pattern, extend(getDefaultConfig(), config)); + }; + + /** + * @ngdoc function + * @name ui.router.util.$urlMatcherFactory#isMatcher + * @methodOf ui.router.util.$urlMatcherFactory + * + * @description + * Returns true if the specified object is a `UrlMatcher`, or false otherwise. + * + * @param {Object} object The object to perform the type check against. + * @returns {Boolean} Returns `true` if the object matches the `UrlMatcher` interface, by + * implementing all the same methods. + */ + this.isMatcher = function (o) { + if (!isObject(o)) return false; + var result = true; + + forEach(UrlMatcher.prototype, function(val, name) { + if (isFunction(val)) { + result = result && (isDefined(o[name]) && isFunction(o[name])); + } + }); + return result; + }; + + /** + * @ngdoc function + * @name ui.router.util.$urlMatcherFactory#type + * @methodOf ui.router.util.$urlMatcherFactory + * + * @description + * Registers a custom {@link ui.router.util.type:Type `Type`} object that can be used to + * generate URLs with typed parameters. + * + * @param {string} name The type name. + * @param {Object|Function} definition The type definition. See + * {@link ui.router.util.type:Type `Type`} for information on the values accepted. + * @param {Object|Function} definitionFn (optional) A function that is injected before the app + * runtime starts. The result of this function is merged into the existing `definition`. + * See {@link ui.router.util.type:Type `Type`} for information on the values accepted. + * + * @returns {Object} Returns `$urlMatcherFactoryProvider`. + * + * @example + * This is a simple example of a custom type that encodes and decodes items from an + * array, using the array index as the URL-encoded value: + * + *
    +   * var list = ['John', 'Paul', 'George', 'Ringo'];
    +   *
    +   * $urlMatcherFactoryProvider.type('listItem', {
    +   *   encode: function(item) {
    +   *     // Represent the list item in the URL using its corresponding index
    +   *     return list.indexOf(item);
    +   *   },
    +   *   decode: function(item) {
    +   *     // Look up the list item by index
    +   *     return list[parseInt(item, 10)];
    +   *   },
    +   *   is: function(item) {
    +   *     // Ensure the item is valid by checking to see that it appears
    +   *     // in the list
    +   *     return list.indexOf(item) > -1;
    +   *   }
    +   * });
    +   *
    +   * $stateProvider.state('list', {
    +   *   url: "/list/{item:listItem}",
    +   *   controller: function($scope, $stateParams) {
    +   *     console.log($stateParams.item);
    +   *   }
    +   * });
    +   *
    +   * // ...
    +   *
    +   * // Changes URL to '/list/3', logs "Ringo" to the console
    +   * $state.go('list', { item: "Ringo" });
    +   * 
    + * + * This is a more complex example of a type that relies on dependency injection to + * interact with services, and uses the parameter name from the URL to infer how to + * handle encoding and decoding parameter values: + * + *
    +   * // Defines a custom type that gets a value from a service,
    +   * // where each service gets different types of values from
    +   * // a backend API:
    +   * $urlMatcherFactoryProvider.type('dbObject', {}, function(Users, Posts) {
    +   *
    +   *   // Matches up services to URL parameter names
    +   *   var services = {
    +   *     user: Users,
    +   *     post: Posts
    +   *   };
    +   *
    +   *   return {
    +   *     encode: function(object) {
    +   *       // Represent the object in the URL using its unique ID
    +   *       return object.id;
    +   *     },
    +   *     decode: function(value, key) {
    +   *       // Look up the object by ID, using the parameter
    +   *       // name (key) to call the correct service
    +   *       return services[key].findById(value);
    +   *     },
    +   *     is: function(object, key) {
    +   *       // Check that object is a valid dbObject
    +   *       return angular.isObject(object) && object.id && services[key];
    +   *     }
    +   *     equals: function(a, b) {
    +   *       // Check the equality of decoded objects by comparing
    +   *       // their unique IDs
    +   *       return a.id === b.id;
    +   *     }
    +   *   };
    +   * });
    +   *
    +   * // In a config() block, you can then attach URLs with
    +   * // type-annotated parameters:
    +   * $stateProvider.state('users', {
    +   *   url: "/users",
    +   *   // ...
    +   * }).state('users.item', {
    +   *   url: "/{user:dbObject}",
    +   *   controller: function($scope, $stateParams) {
    +   *     // $stateParams.user will now be an object returned from
    +   *     // the Users service
    +   *   },
    +   *   // ...
    +   * });
    +   * 
    + */ + this.type = function (name, definition, definitionFn) { + if (!isDefined(definition)) return $types[name]; + if ($types.hasOwnProperty(name)) throw new Error("A type named '" + name + "' has already been defined."); + + $types[name] = new Type(extend({ name: name }, definition)); + if (definitionFn) { + typeQueue.push({ name: name, def: definitionFn }); + if (!enqueue) flushTypeQueue(); + } + return this; + }; + + // `flushTypeQueue()` waits until `$urlMatcherFactory` is injected before invoking the queued `definitionFn`s + function flushTypeQueue() { + while(typeQueue.length) { + var type = typeQueue.shift(); + if (type.pattern) throw new Error("You cannot override a type's .pattern at runtime."); + angular.extend($types[type.name], injector.invoke(type.def)); + } + } + + // Register default types. Store them in the prototype of $types. + forEach(defaultTypes, function(type, name) { $types[name] = new Type(extend({name: name}, type)); }); + $types = inherit($types, {}); + + /* No need to document $get, since it returns this */ + this.$get = ['$injector', function ($injector) { + injector = $injector; + enqueue = false; + flushTypeQueue(); + + forEach(defaultTypes, function(type, name) { + if (!$types[name]) $types[name] = new Type(type); + }); + return this; + }]; + + this.Param = function Param(id, type, config, location) { + var self = this; + config = unwrapShorthand(config); + type = getType(config, type, location); + var arrayMode = getArrayMode(); + type = arrayMode ? type.$asArray(arrayMode, location === "search") : type; + if (type.name === "string" && !arrayMode && location === "path" && config.value === undefined) + config.value = ""; // for 0.2.x; in 0.3.0+ do not automatically default to "" + var isOptional = config.value !== undefined; + var squash = getSquashPolicy(config, isOptional); + var replace = getReplace(config, arrayMode, isOptional, squash); + + function unwrapShorthand(config) { + var keys = isObject(config) ? objectKeys(config) : []; + var isShorthand = indexOf(keys, "value") === -1 && indexOf(keys, "type") === -1 && + indexOf(keys, "squash") === -1 && indexOf(keys, "array") === -1; + if (isShorthand) config = { value: config }; + config.$$fn = isInjectable(config.value) ? config.value : function () { return config.value; }; + return config; + } + + function getType(config, urlType, location) { + if (config.type && urlType) throw new Error("Param '"+id+"' has two type configurations."); + if (urlType) return urlType; + if (!config.type) return (location === "config" ? $types.any : $types.string); + return config.type instanceof Type ? config.type : new Type(config.type); + } + + // array config: param name (param[]) overrides default settings. explicit config overrides param name. + function getArrayMode() { + var arrayDefaults = { array: (location === "search" ? "auto" : false) }; + var arrayParamNomenclature = id.match(/\[\]$/) ? { array: true } : {}; + return extend(arrayDefaults, arrayParamNomenclature, config).array; + } + + /** + * returns false, true, or the squash value to indicate the "default parameter url squash policy". + */ + function getSquashPolicy(config, isOptional) { + var squash = config.squash; + if (!isOptional || squash === false) return false; + if (!isDefined(squash) || squash == null) return defaultSquashPolicy; + if (squash === true || isString(squash)) return squash; + throw new Error("Invalid squash policy: '" + squash + "'. Valid policies: false, true, or arbitrary string"); + } + + function getReplace(config, arrayMode, isOptional, squash) { + var replace, configuredKeys, defaultPolicy = [ + { from: "", to: (isOptional || arrayMode ? undefined : "") }, + { from: null, to: (isOptional || arrayMode ? undefined : "") } + ]; + replace = isArray(config.replace) ? config.replace : []; + if (isString(squash)) + replace.push({ from: squash, to: undefined }); + configuredKeys = map(replace, function(item) { return item.from; } ); + return filter(defaultPolicy, function(item) { return indexOf(configuredKeys, item.from) === -1; }).concat(replace); + } + + /** + * [Internal] Get the default value of a parameter, which may be an injectable function. + */ + function $$getDefaultValue() { + if (!injector) throw new Error("Injectable functions cannot be called at configuration time"); + return injector.invoke(config.$$fn); + } + + /** + * [Internal] Gets the decoded representation of a value if the value is defined, otherwise, returns the + * default value, which may be the result of an injectable function. + */ + function $value(value) { + function hasReplaceVal(val) { return function(obj) { return obj.from === val; }; } + function $replace(value) { + var replacement = map(filter(self.replace, hasReplaceVal(value)), function(obj) { return obj.to; }); + return replacement.length ? replacement[0] : value; + } + value = $replace(value); + return isDefined(value) ? self.type.decode(value) : $$getDefaultValue(); + } + + function toString() { return "{Param:" + id + " " + type + " squash: '" + squash + "' optional: " + isOptional + "}"; } + + extend(this, { + id: id, + type: type, + location: location, + array: arrayMode, + squash: squash, + replace: replace, + isOptional: isOptional, + value: $value, + dynamic: undefined, + config: config, + toString: toString + }); + }; + + function ParamSet(params) { + extend(this, params || {}); + } + + ParamSet.prototype = { + $$new: function() { + return inherit(this, extend(new ParamSet(), { $$parent: this})); + }, + $$keys: function () { + var keys = [], chain = [], parent = this, + ignore = objectKeys(ParamSet.prototype); + while (parent) { chain.push(parent); parent = parent.$$parent; } + chain.reverse(); + forEach(chain, function(paramset) { + forEach(objectKeys(paramset), function(key) { + if (indexOf(keys, key) === -1 && indexOf(ignore, key) === -1) keys.push(key); + }); + }); + return keys; + }, + $$values: function(paramValues) { + var values = {}, self = this; + forEach(self.$$keys(), function(key) { + values[key] = self[key].value(paramValues && paramValues[key]); + }); + return values; + }, + $$equals: function(paramValues1, paramValues2) { + var equal = true, self = this; + forEach(self.$$keys(), function(key) { + var left = paramValues1 && paramValues1[key], right = paramValues2 && paramValues2[key]; + if (!self[key].type.equals(left, right)) equal = false; + }); + return equal; + }, + $$validates: function $$validate(paramValues) { + var result = true, isOptional, val, param, self = this; + + forEach(this.$$keys(), function(key) { + param = self[key]; + val = paramValues[key]; + isOptional = !val && param.isOptional; + result = result && (isOptional || !!param.type.is(val)); + }); + return result; + }, + $$parent: undefined + }; + + this.ParamSet = ParamSet; +} + +// Register as a provider so it's available to other providers +angular.module('ui.router.util').provider('$urlMatcherFactory', $UrlMatcherFactory); +angular.module('ui.router.util').run(['$urlMatcherFactory', function($urlMatcherFactory) { }]); + +/** + * @ngdoc object + * @name ui.router.router.$urlRouterProvider + * + * @requires ui.router.util.$urlMatcherFactoryProvider + * @requires $locationProvider + * + * @description + * `$urlRouterProvider` has the responsibility of watching `$location`. + * When `$location` changes it runs through a list of rules one by one until a + * match is found. `$urlRouterProvider` is used behind the scenes anytime you specify + * a url in a state configuration. All urls are compiled into a UrlMatcher object. + * + * There are several methods on `$urlRouterProvider` that make it useful to use directly + * in your module config. + */ +$UrlRouterProvider.$inject = ['$locationProvider', '$urlMatcherFactoryProvider']; +function $UrlRouterProvider( $locationProvider, $urlMatcherFactory) { + var rules = [], otherwise = null, interceptDeferred = false, listener; + + // Returns a string that is a prefix of all strings matching the RegExp + function regExpPrefix(re) { + var prefix = /^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(re.source); + return (prefix != null) ? prefix[1].replace(/\\(.)/g, "$1") : ''; + } + + // Interpolates matched values into a String.replace()-style pattern + function interpolate(pattern, match) { + return pattern.replace(/\$(\$|\d{1,2})/, function (m, what) { + return match[what === '$' ? 0 : Number(what)]; + }); + } + + /** + * @ngdoc function + * @name ui.router.router.$urlRouterProvider#rule + * @methodOf ui.router.router.$urlRouterProvider + * + * @description + * Defines rules that are used by `$urlRouterProvider` to find matches for + * specific URLs. + * + * @example + *
    +   * var app = angular.module('app', ['ui.router.router']);
    +   *
    +   * app.config(function ($urlRouterProvider) {
    +   *   // Here's an example of how you might allow case insensitive urls
    +   *   $urlRouterProvider.rule(function ($injector, $location) {
    +   *     var path = $location.path(),
    +   *         normalized = path.toLowerCase();
    +   *
    +   *     if (path !== normalized) {
    +   *       return normalized;
    +   *     }
    +   *   });
    +   * });
    +   * 
    + * + * @param {object} rule Handler function that takes `$injector` and `$location` + * services as arguments. You can use them to return a valid path as a string. + * + * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance + */ + this.rule = function (rule) { + if (!isFunction(rule)) throw new Error("'rule' must be a function"); + rules.push(rule); + return this; + }; + + /** + * @ngdoc object + * @name ui.router.router.$urlRouterProvider#otherwise + * @methodOf ui.router.router.$urlRouterProvider + * + * @description + * Defines a path that is used when an invalid route is requested. + * + * @example + *
    +   * var app = angular.module('app', ['ui.router.router']);
    +   *
    +   * app.config(function ($urlRouterProvider) {
    +   *   // if the path doesn't match any of the urls you configured
    +   *   // otherwise will take care of routing the user to the
    +   *   // specified url
    +   *   $urlRouterProvider.otherwise('/index');
    +   *
    +   *   // Example of using function rule as param
    +   *   $urlRouterProvider.otherwise(function ($injector, $location) {
    +   *     return '/a/valid/url';
    +   *   });
    +   * });
    +   * 
    + * + * @param {string|object} rule The url path you want to redirect to or a function + * rule that returns the url path. The function version is passed two params: + * `$injector` and `$location` services, and must return a url string. + * + * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance + */ + this.otherwise = function (rule) { + if (isString(rule)) { + var redirect = rule; + rule = function () { return redirect; }; + } + else if (!isFunction(rule)) throw new Error("'rule' must be a function"); + otherwise = rule; + return this; + }; + + + function handleIfMatch($injector, handler, match) { + if (!match) return false; + var result = $injector.invoke(handler, handler, { $match: match }); + return isDefined(result) ? result : true; + } + + /** + * @ngdoc function + * @name ui.router.router.$urlRouterProvider#when + * @methodOf ui.router.router.$urlRouterProvider + * + * @description + * Registers a handler for a given url matching. if handle is a string, it is + * treated as a redirect, and is interpolated according to the syntax of match + * (i.e. like `String.replace()` for `RegExp`, or like a `UrlMatcher` pattern otherwise). + * + * If the handler is a function, it is injectable. It gets invoked if `$location` + * matches. You have the option of inject the match object as `$match`. + * + * The handler can return + * + * - **falsy** to indicate that the rule didn't match after all, then `$urlRouter` + * will continue trying to find another one that matches. + * - **string** which is treated as a redirect and passed to `$location.url()` + * - **void** or any **truthy** value tells `$urlRouter` that the url was handled. + * + * @example + *
    +   * var app = angular.module('app', ['ui.router.router']);
    +   *
    +   * app.config(function ($urlRouterProvider) {
    +   *   $urlRouterProvider.when($state.url, function ($match, $stateParams) {
    +   *     if ($state.$current.navigable !== state ||
    +   *         !equalForKeys($match, $stateParams) {
    +   *      $state.transitionTo(state, $match, false);
    +   *     }
    +   *   });
    +   * });
    +   * 
    + * + * @param {string|object} what The incoming path that you want to redirect. + * @param {string|object} handler The path you want to redirect your user to. + */ + this.when = function (what, handler) { + var redirect, handlerIsString = isString(handler); + if (isString(what)) what = $urlMatcherFactory.compile(what); + + if (!handlerIsString && !isFunction(handler) && !isArray(handler)) + throw new Error("invalid 'handler' in when()"); + + var strategies = { + matcher: function (what, handler) { + if (handlerIsString) { + redirect = $urlMatcherFactory.compile(handler); + handler = ['$match', function ($match) { return redirect.format($match); }]; + } + return extend(function ($injector, $location) { + return handleIfMatch($injector, handler, what.exec($location.path(), $location.search())); + }, { + prefix: isString(what.prefix) ? what.prefix : '' + }); + }, + regex: function (what, handler) { + if (what.global || what.sticky) throw new Error("when() RegExp must not be global or sticky"); + + if (handlerIsString) { + redirect = handler; + handler = ['$match', function ($match) { return interpolate(redirect, $match); }]; + } + return extend(function ($injector, $location) { + return handleIfMatch($injector, handler, what.exec($location.path())); + }, { + prefix: regExpPrefix(what) + }); + } + }; + + var check = { matcher: $urlMatcherFactory.isMatcher(what), regex: what instanceof RegExp }; + + for (var n in check) { + if (check[n]) return this.rule(strategies[n](what, handler)); + } + + throw new Error("invalid 'what' in when()"); + }; + + /** + * @ngdoc function + * @name ui.router.router.$urlRouterProvider#deferIntercept + * @methodOf ui.router.router.$urlRouterProvider + * + * @description + * Disables (or enables) deferring location change interception. + * + * If you wish to customize the behavior of syncing the URL (for example, if you wish to + * defer a transition but maintain the current URL), call this method at configuration time. + * Then, at run time, call `$urlRouter.listen()` after you have configured your own + * `$locationChangeSuccess` event handler. + * + * @example + *
    +   * var app = angular.module('app', ['ui.router.router']);
    +   *
    +   * app.config(function ($urlRouterProvider) {
    +   *
    +   *   // Prevent $urlRouter from automatically intercepting URL changes;
    +   *   // this allows you to configure custom behavior in between
    +   *   // location changes and route synchronization:
    +   *   $urlRouterProvider.deferIntercept();
    +   *
    +   * }).run(function ($rootScope, $urlRouter, UserService) {
    +   *
    +   *   $rootScope.$on('$locationChangeSuccess', function(e) {
    +   *     // UserService is an example service for managing user state
    +   *     if (UserService.isLoggedIn()) return;
    +   *
    +   *     // Prevent $urlRouter's default handler from firing
    +   *     e.preventDefault();
    +   *
    +   *     UserService.handleLogin().then(function() {
    +   *       // Once the user has logged in, sync the current URL
    +   *       // to the router:
    +   *       $urlRouter.sync();
    +   *     });
    +   *   });
    +   *
    +   *   // Configures $urlRouter's listener *after* your custom listener
    +   *   $urlRouter.listen();
    +   * });
    +   * 
    + * + * @param {boolean} defer Indicates whether to defer location change interception. Passing + no parameter is equivalent to `true`. + */ + this.deferIntercept = function (defer) { + if (defer === undefined) defer = true; + interceptDeferred = defer; + }; + + /** + * @ngdoc object + * @name ui.router.router.$urlRouter + * + * @requires $location + * @requires $rootScope + * @requires $injector + * @requires $browser + * + * @description + * + */ + this.$get = $get; + $get.$inject = ['$location', '$rootScope', '$injector', '$browser']; + function $get( $location, $rootScope, $injector, $browser) { + + var baseHref = $browser.baseHref(), location = $location.url(), lastPushedUrl; + + function appendBasePath(url, isHtml5, absolute) { + if (baseHref === '/') return url; + if (isHtml5) return baseHref.slice(0, -1) + url; + if (absolute) return baseHref.slice(1) + url; + return url; + } + + // TODO: Optimize groups of rules with non-empty prefix into some sort of decision tree + function update(evt) { + if (evt && evt.defaultPrevented) return; + var ignoreUpdate = lastPushedUrl && $location.url() === lastPushedUrl; + lastPushedUrl = undefined; + if (ignoreUpdate) return true; + + function check(rule) { + var handled = rule($injector, $location); + + if (!handled) return false; + if (isString(handled)) $location.replace().url(handled); + return true; + } + var n = rules.length, i; + + for (i = 0; i < n; i++) { + if (check(rules[i])) return; + } + // always check otherwise last to allow dynamic updates to the set of rules + if (otherwise) check(otherwise); + } + + function listen() { + listener = listener || $rootScope.$on('$locationChangeSuccess', update); + return listener; + } + + if (!interceptDeferred) listen(); + + return { + /** + * @ngdoc function + * @name ui.router.router.$urlRouter#sync + * @methodOf ui.router.router.$urlRouter + * + * @description + * Triggers an update; the same update that happens when the address bar url changes, aka `$locationChangeSuccess`. + * This method is useful when you need to use `preventDefault()` on the `$locationChangeSuccess` event, + * perform some custom logic (route protection, auth, config, redirection, etc) and then finally proceed + * with the transition by calling `$urlRouter.sync()`. + * + * @example + *
    +       * angular.module('app', ['ui.router'])
    +       *   .run(function($rootScope, $urlRouter) {
    +       *     $rootScope.$on('$locationChangeSuccess', function(evt) {
    +       *       // Halt state change from even starting
    +       *       evt.preventDefault();
    +       *       // Perform custom logic
    +       *       var meetsRequirement = ...
    +       *       // Continue with the update and state transition if logic allows
    +       *       if (meetsRequirement) $urlRouter.sync();
    +       *     });
    +       * });
    +       * 
    + */ + sync: function() { + update(); + }, + + listen: function() { + return listen(); + }, + + update: function(read) { + if (read) { + location = $location.url(); + return; + } + if ($location.url() === location) return; + + $location.url(location); + $location.replace(); + }, + + push: function(urlMatcher, params, options) { + $location.url(urlMatcher.format(params || {})); + lastPushedUrl = options && options.$$avoidResync ? $location.url() : undefined; + if (options && options.replace) $location.replace(); + }, + + /** + * @ngdoc function + * @name ui.router.router.$urlRouter#href + * @methodOf ui.router.router.$urlRouter + * + * @description + * A URL generation method that returns the compiled URL for a given + * {@link ui.router.util.type:UrlMatcher `UrlMatcher`}, populated with the provided parameters. + * + * @example + *
    +       * $bob = $urlRouter.href(new UrlMatcher("/about/:person"), {
    +       *   person: "bob"
    +       * });
    +       * // $bob == "/about/bob";
    +       * 
    + * + * @param {UrlMatcher} urlMatcher The `UrlMatcher` object which is used as the template of the URL to generate. + * @param {object=} params An object of parameter values to fill the matcher's required parameters. + * @param {object=} options Options object. The options are: + * + * - **`absolute`** - {boolean=false}, If true will generate an absolute url, e.g. "http://www.example.com/fullurl". + * + * @returns {string} Returns the fully compiled URL, or `null` if `params` fail validation against `urlMatcher` + */ + href: function(urlMatcher, params, options) { + if (!urlMatcher.validates(params)) return null; + + var isHtml5 = $locationProvider.html5Mode(); + if (angular.isObject(isHtml5)) { + isHtml5 = isHtml5.enabled; + } + + var url = urlMatcher.format(params); + options = options || {}; + + if (!isHtml5 && url !== null) { + url = "#" + $locationProvider.hashPrefix() + url; + } + url = appendBasePath(url, isHtml5, options.absolute); + + if (!options.absolute || !url) { + return url; + } + + var slash = (!isHtml5 && url ? '/' : ''), port = $location.port(); + port = (port === 80 || port === 443 ? '' : ':' + port); + + return [$location.protocol(), '://', $location.host(), port, slash, url].join(''); + } + }; + } +} + +angular.module('ui.router.router').provider('$urlRouter', $UrlRouterProvider); + +/** + * @ngdoc object + * @name ui.router.state.$stateProvider + * + * @requires ui.router.router.$urlRouterProvider + * @requires ui.router.util.$urlMatcherFactoryProvider + * + * @description + * The new `$stateProvider` works similar to Angular's v1 router, but it focuses purely + * on state. + * + * A state corresponds to a "place" in the application in terms of the overall UI and + * navigation. A state describes (via the controller / template / view properties) what + * the UI looks like and does at that place. + * + * States often have things in common, and the primary way of factoring out these + * commonalities in this model is via the state hierarchy, i.e. parent/child states aka + * nested states. + * + * The `$stateProvider` provides interfaces to declare these states for your app. + */ +$StateProvider.$inject = ['$urlRouterProvider', '$urlMatcherFactoryProvider']; +function $StateProvider( $urlRouterProvider, $urlMatcherFactory) { + + var root, states = {}, $state, queue = {}, abstractKey = 'abstract'; + + // Builds state properties from definition passed to registerState() + var stateBuilder = { + + // Derive parent state from a hierarchical name only if 'parent' is not explicitly defined. + // state.children = []; + // if (parent) parent.children.push(state); + parent: function(state) { + if (isDefined(state.parent) && state.parent) return findState(state.parent); + // regex matches any valid composite state name + // would match "contact.list" but not "contacts" + var compositeName = /^(.+)\.[^.]+$/.exec(state.name); + return compositeName ? findState(compositeName[1]) : root; + }, + + // inherit 'data' from parent and override by own values (if any) + data: function(state) { + if (state.parent && state.parent.data) { + state.data = state.self.data = extend({}, state.parent.data, state.data); + } + return state.data; + }, + + // Build a URLMatcher if necessary, either via a relative or absolute URL + url: function(state) { + var url = state.url, config = { params: state.params || {} }; + + if (isString(url)) { + if (url.charAt(0) == '^') return $urlMatcherFactory.compile(url.substring(1), config); + return (state.parent.navigable || root).url.concat(url, config); + } + + if (!url || $urlMatcherFactory.isMatcher(url)) return url; + throw new Error("Invalid url '" + url + "' in state '" + state + "'"); + }, + + // Keep track of the closest ancestor state that has a URL (i.e. is navigable) + navigable: function(state) { + return state.url ? state : (state.parent ? state.parent.navigable : null); + }, + + // Own parameters for this state. state.url.params is already built at this point. Create and add non-url params + ownParams: function(state) { + var params = state.url && state.url.params || new $$UMFP.ParamSet(); + forEach(state.params || {}, function(config, id) { + if (!params[id]) params[id] = new $$UMFP.Param(id, null, config, "config"); + }); + return params; + }, + + // Derive parameters for this state and ensure they're a super-set of parent's parameters + params: function(state) { + return state.parent && state.parent.params ? extend(state.parent.params.$$new(), state.ownParams) : new $$UMFP.ParamSet(); + }, + + // If there is no explicit multi-view configuration, make one up so we don't have + // to handle both cases in the view directive later. Note that having an explicit + // 'views' property will mean the default unnamed view properties are ignored. This + // is also a good time to resolve view names to absolute names, so everything is a + // straight lookup at link time. + views: function(state) { + var views = {}; + + forEach(isDefined(state.views) ? state.views : { '': state }, function (view, name) { + if (name.indexOf('@') < 0) name += '@' + state.parent.name; + views[name] = view; + }); + return views; + }, + + // Keep a full path from the root down to this state as this is needed for state activation. + path: function(state) { + return state.parent ? state.parent.path.concat(state) : []; // exclude root from path + }, + + // Speed up $state.contains() as it's used a lot + includes: function(state) { + var includes = state.parent ? extend({}, state.parent.includes) : {}; + includes[state.name] = true; + return includes; + }, + + $delegates: {} + }; + + function isRelative(stateName) { + return stateName.indexOf(".") === 0 || stateName.indexOf("^") === 0; + } + + function findState(stateOrName, base) { + if (!stateOrName) return undefined; + + var isStr = isString(stateOrName), + name = isStr ? stateOrName : stateOrName.name, + path = isRelative(name); + + if (path) { + if (!base) throw new Error("No reference point given for path '" + name + "'"); + base = findState(base); + + var rel = name.split("."), i = 0, pathLength = rel.length, current = base; + + for (; i < pathLength; i++) { + if (rel[i] === "" && i === 0) { + current = base; + continue; + } + if (rel[i] === "^") { + if (!current.parent) throw new Error("Path '" + name + "' not valid for state '" + base.name + "'"); + current = current.parent; + continue; + } + break; + } + rel = rel.slice(i).join("."); + name = current.name + (current.name && rel ? "." : "") + rel; + } + var state = states[name]; + + if (state && (isStr || (!isStr && (state === stateOrName || state.self === stateOrName)))) { + return state; + } + return undefined; + } + + function queueState(parentName, state) { + if (!queue[parentName]) { + queue[parentName] = []; + } + queue[parentName].push(state); + } + + function flushQueuedChildren(parentName) { + var queued = queue[parentName] || []; + while(queued.length) { + registerState(queued.shift()); + } + } + + function registerState(state) { + // Wrap a new object around the state so we can store our private details easily. + state = inherit(state, { + self: state, + resolve: state.resolve || {}, + toString: function() { return this.name; } + }); + + var name = state.name; + if (!isString(name) || name.indexOf('@') >= 0) throw new Error("State must have a valid name"); + if (states.hasOwnProperty(name)) throw new Error("State '" + name + "'' is already defined"); + + // Get parent name + var parentName = (name.indexOf('.') !== -1) ? name.substring(0, name.lastIndexOf('.')) + : (isString(state.parent)) ? state.parent + : (isObject(state.parent) && isString(state.parent.name)) ? state.parent.name + : ''; + + // If parent is not registered yet, add state to queue and register later + if (parentName && !states[parentName]) { + return queueState(parentName, state.self); + } + + for (var key in stateBuilder) { + if (isFunction(stateBuilder[key])) state[key] = stateBuilder[key](state, stateBuilder.$delegates[key]); + } + states[name] = state; + + // Register the state in the global state list and with $urlRouter if necessary. + if (!state[abstractKey] && state.url) { + $urlRouterProvider.when(state.url, ['$match', '$stateParams', function ($match, $stateParams) { + if ($state.$current.navigable != state || !equalForKeys($match, $stateParams)) { + $state.transitionTo(state, $match, { inherit: true, location: false }); + } + }]); + } + + // Register any queued children + flushQueuedChildren(name); + + return state; + } + + // Checks text to see if it looks like a glob. + function isGlob (text) { + return text.indexOf('*') > -1; + } + + // Returns true if glob matches current $state name. + function doesStateMatchGlob (glob) { + var globSegments = glob.split('.'), + segments = $state.$current.name.split('.'); + + //match greedy starts + if (globSegments[0] === '**') { + segments = segments.slice(indexOf(segments, globSegments[1])); + segments.unshift('**'); + } + //match greedy ends + if (globSegments[globSegments.length - 1] === '**') { + segments.splice(indexOf(segments, globSegments[globSegments.length - 2]) + 1, Number.MAX_VALUE); + segments.push('**'); + } + + if (globSegments.length != segments.length) { + return false; + } + + //match single stars + for (var i = 0, l = globSegments.length; i < l; i++) { + if (globSegments[i] === '*') { + segments[i] = '*'; + } + } + + return segments.join('') === globSegments.join(''); + } + + + // Implicit root state that is always active + root = registerState({ + name: '', + url: '^', + views: null, + 'abstract': true + }); + root.navigable = null; + + + /** + * @ngdoc function + * @name ui.router.state.$stateProvider#decorator + * @methodOf ui.router.state.$stateProvider + * + * @description + * Allows you to extend (carefully) or override (at your own peril) the + * `stateBuilder` object used internally by `$stateProvider`. This can be used + * to add custom functionality to ui-router, for example inferring templateUrl + * based on the state name. + * + * When passing only a name, it returns the current (original or decorated) builder + * function that matches `name`. + * + * The builder functions that can be decorated are listed below. Though not all + * necessarily have a good use case for decoration, that is up to you to decide. + * + * In addition, users can attach custom decorators, which will generate new + * properties within the state's internal definition. There is currently no clear + * use-case for this beyond accessing internal states (i.e. $state.$current), + * however, expect this to become increasingly relevant as we introduce additional + * meta-programming features. + * + * **Warning**: Decorators should not be interdependent because the order of + * execution of the builder functions in non-deterministic. Builder functions + * should only be dependent on the state definition object and super function. + * + * + * Existing builder functions and current return values: + * + * - **parent** `{object}` - returns the parent state object. + * - **data** `{object}` - returns state data, including any inherited data that is not + * overridden by own values (if any). + * - **url** `{object}` - returns a {@link ui.router.util.type:UrlMatcher UrlMatcher} + * or `null`. + * - **navigable** `{object}` - returns closest ancestor state that has a URL (aka is + * navigable). + * - **params** `{object}` - returns an array of state params that are ensured to + * be a super-set of parent's params. + * - **views** `{object}` - returns a views object where each key is an absolute view + * name (i.e. "viewName@stateName") and each value is the config object + * (template, controller) for the view. Even when you don't use the views object + * explicitly on a state config, one is still created for you internally. + * So by decorating this builder function you have access to decorating template + * and controller properties. + * - **ownParams** `{object}` - returns an array of params that belong to the state, + * not including any params defined by ancestor states. + * - **path** `{string}` - returns the full path from the root down to this state. + * Needed for state activation. + * - **includes** `{object}` - returns an object that includes every state that + * would pass a `$state.includes()` test. + * + * @example + *
    +   * // Override the internal 'views' builder with a function that takes the state
    +   * // definition, and a reference to the internal function being overridden:
    +   * $stateProvider.decorator('views', function (state, parent) {
    +   *   var result = {},
    +   *       views = parent(state);
    +   *
    +   *   angular.forEach(views, function (config, name) {
    +   *     var autoName = (state.name + '.' + name).replace('.', '/');
    +   *     config.templateUrl = config.templateUrl || '/partials/' + autoName + '.html';
    +   *     result[name] = config;
    +   *   });
    +   *   return result;
    +   * });
    +   *
    +   * $stateProvider.state('home', {
    +   *   views: {
    +   *     'contact.list': { controller: 'ListController' },
    +   *     'contact.item': { controller: 'ItemController' }
    +   *   }
    +   * });
    +   *
    +   * // ...
    +   *
    +   * $state.go('home');
    +   * // Auto-populates list and item views with /partials/home/contact/list.html,
    +   * // and /partials/home/contact/item.html, respectively.
    +   * 
    + * + * @param {string} name The name of the builder function to decorate. + * @param {object} func A function that is responsible for decorating the original + * builder function. The function receives two parameters: + * + * - `{object}` - state - The state config object. + * - `{object}` - super - The original builder function. + * + * @return {object} $stateProvider - $stateProvider instance + */ + this.decorator = decorator; + function decorator(name, func) { + /*jshint validthis: true */ + if (isString(name) && !isDefined(func)) { + return stateBuilder[name]; + } + if (!isFunction(func) || !isString(name)) { + return this; + } + if (stateBuilder[name] && !stateBuilder.$delegates[name]) { + stateBuilder.$delegates[name] = stateBuilder[name]; + } + stateBuilder[name] = func; + return this; + } + + /** + * @ngdoc function + * @name ui.router.state.$stateProvider#state + * @methodOf ui.router.state.$stateProvider + * + * @description + * Registers a state configuration under a given state name. The stateConfig object + * has the following acceptable properties. + * + * @param {string} name A unique state name, e.g. "home", "about", "contacts". + * To create a parent/child state use a dot, e.g. "about.sales", "home.newest". + * @param {object} stateConfig State configuration object. + * @param {string|function=} stateConfig.template + *
    + * html template as a string or a function that returns + * an html template as a string which should be used by the uiView directives. This property + * takes precedence over templateUrl. + * + * If `template` is a function, it will be called with the following parameters: + * + * - {array.<object>} - state parameters extracted from the current $location.path() by + * applying the current state + * + *
    template:
    +   *   "

    inline template definition

    " + + * "
    "
    + *
    template: function(params) {
    +   *       return "

    generated template

    "; }
    + * + * + * @param {string|function=} stateConfig.templateUrl + * + * + * path or function that returns a path to an html + * template that should be used by uiView. + * + * If `templateUrl` is a function, it will be called with the following parameters: + * + * - {array.<object>} - state parameters extracted from the current $location.path() by + * applying the current state + * + *
    templateUrl: "home.html"
    + *
    templateUrl: function(params) {
    +   *     return myTemplates[params.pageId]; }
    + * + * @param {function=} stateConfig.templateProvider + * + * Provider function that returns HTML content string. + *
     templateProvider:
    +   *       function(MyTemplateService, params) {
    +   *         return MyTemplateService.getTemplate(params.pageId);
    +   *       }
    + * + * @param {string|function=} stateConfig.controller + * + * + * Controller fn that should be associated with newly + * related scope or the name of a registered controller if passed as a string. + * Optionally, the ControllerAs may be declared here. + *
    controller: "MyRegisteredController"
    + *
    controller:
    +   *     "MyRegisteredController as fooCtrl"}
    + *
    controller: function($scope, MyService) {
    +   *     $scope.data = MyService.getData(); }
    + * + * @param {function=} stateConfig.controllerProvider + * + * + * Injectable provider function that returns the actual controller or string. + *
    controllerProvider:
    +   *   function(MyResolveData) {
    +   *     if (MyResolveData.foo)
    +   *       return "FooCtrl"
    +   *     else if (MyResolveData.bar)
    +   *       return "BarCtrl";
    +   *     else return function($scope) {
    +   *       $scope.baz = "Qux";
    +   *     }
    +   *   }
    + * + * @param {string=} stateConfig.controllerAs + * + * + * A controller alias name. If present the controller will be + * published to scope under the controllerAs name. + *
    controllerAs: "myCtrl"
    + * + * @param {object=} stateConfig.resolve + * + * + * An optional map<string, function> of dependencies which + * should be injected into the controller. If any of these dependencies are promises, + * the router will wait for them all to be resolved before the controller is instantiated. + * If all the promises are resolved successfully, the $stateChangeSuccess event is fired + * and the values of the resolved promises are injected into any controllers that reference them. + * If any of the promises are rejected the $stateChangeError event is fired. + * + * The map object is: + * + * - key - {string}: name of dependency to be injected into controller + * - factory - {string|function}: If string then it is alias for service. Otherwise if function, + * it is injected and return value it treated as dependency. If result is a promise, it is + * resolved before its value is injected into controller. + * + *
    resolve: {
    +   *     myResolve1:
    +   *       function($http, $stateParams) {
    +   *         return $http.get("/api/foos/"+stateParams.fooID);
    +   *       }
    +   *     }
    + * + * @param {string=} stateConfig.url + * + * + * A url fragment with optional parameters. When a state is navigated or + * transitioned to, the `$stateParams` service will be populated with any + * parameters that were passed. + * + * examples: + *
    url: "/home"
    +   * url: "/users/:userid"
    +   * url: "/books/{bookid:[a-zA-Z_-]}"
    +   * url: "/books/{categoryid:int}"
    +   * url: "/books/{publishername:string}/{categoryid:int}"
    +   * url: "/messages?before&after"
    +   * url: "/messages?{before:date}&{after:date}"
    + * url: "/messages/:mailboxid?{before:date}&{after:date}" + * + * @param {object=} stateConfig.views + * + * an optional map<string, object> which defined multiple views, or targets views + * manually/explicitly. + * + * Examples: + * + * Targets three named `ui-view`s in the parent state's template + *
    views: {
    +   *     header: {
    +   *       controller: "headerCtrl",
    +   *       templateUrl: "header.html"
    +   *     }, body: {
    +   *       controller: "bodyCtrl",
    +   *       templateUrl: "body.html"
    +   *     }, footer: {
    +   *       controller: "footCtrl",
    +   *       templateUrl: "footer.html"
    +   *     }
    +   *   }
    + * + * Targets named `ui-view="header"` from grandparent state 'top''s template, and named `ui-view="body" from parent state's template. + *
    views: {
    +   *     'header@top': {
    +   *       controller: "msgHeaderCtrl",
    +   *       templateUrl: "msgHeader.html"
    +   *     }, 'body': {
    +   *       controller: "messagesCtrl",
    +   *       templateUrl: "messages.html"
    +   *     }
    +   *   }
    + * + * @param {boolean=} [stateConfig.abstract=false] + * + * An abstract state will never be directly activated, + * but can provide inherited properties to its common children states. + *
    abstract: true
    + * + * @param {function=} stateConfig.onEnter + * + * + * Callback function for when a state is entered. Good way + * to trigger an action or dispatch an event, such as opening a dialog. + * If minifying your scripts, make sure to explictly annotate this function, + * because it won't be automatically annotated by your build tools. + * + *
    onEnter: function(MyService, $stateParams) {
    +   *     MyService.foo($stateParams.myParam);
    +   * }
    + * + * @param {function=} stateConfig.onExit + * + * + * Callback function for when a state is exited. Good way to + * trigger an action or dispatch an event, such as opening a dialog. + * If minifying your scripts, make sure to explictly annotate this function, + * because it won't be automatically annotated by your build tools. + * + *
    onExit: function(MyService, $stateParams) {
    +   *     MyService.cleanup($stateParams.myParam);
    +   * }
    + * + * @param {boolean=} [stateConfig.reloadOnSearch=true] + * + * + * If `false`, will not retrigger the same state + * just because a search/query parameter has changed (via $location.search() or $location.hash()). + * Useful for when you'd like to modify $location.search() without triggering a reload. + *
    reloadOnSearch: false
    + * + * @param {object=} stateConfig.data + * + * + * Arbitrary data object, useful for custom configuration. The parent state's `data` is + * prototypally inherited. In other words, adding a data property to a state adds it to + * the entire subtree via prototypal inheritance. + * + *
    data: {
    +   *     requiredRole: 'foo'
    +   * } 
    + * + * @param {object=} stateConfig.params + * + * + * A map which optionally configures parameters declared in the `url`, or + * defines additional non-url parameters. For each parameter being + * configured, add a configuration object keyed to the name of the parameter. + * + * Each parameter configuration object may contain the following properties: + * + * - ** value ** - {object|function=}: specifies the default value for this + * parameter. This implicitly sets this parameter as optional. + * + * When UI-Router routes to a state and no value is + * specified for this parameter in the URL or transition, the + * default value will be used instead. If `value` is a function, + * it will be injected and invoked, and the return value used. + * + * *Note*: `undefined` is treated as "no default value" while `null` + * is treated as "the default value is `null`". + * + * *Shorthand*: If you only need to configure the default value of the + * parameter, you may use a shorthand syntax. In the **`params`** + * map, instead mapping the param name to a full parameter configuration + * object, simply set map it to the default parameter value, e.g.: + * + *
    // define a parameter's default value
    +   * params: {
    +   *     param1: { value: "defaultValue" }
    +   * }
    +   * // shorthand default values
    +   * params: {
    +   *     param1: "defaultValue",
    +   *     param2: "param2Default"
    +   * }
    + * + * - ** array ** - {boolean=}: *(default: false)* If true, the param value will be + * treated as an array of values. If you specified a Type, the value will be + * treated as an array of the specified Type. Note: query parameter values + * default to a special `"auto"` mode. + * + * For query parameters in `"auto"` mode, if multiple values for a single parameter + * are present in the URL (e.g.: `/foo?bar=1&bar=2&bar=3`) then the values + * are mapped to an array (e.g.: `{ foo: [ '1', '2', '3' ] }`). However, if + * only one value is present (e.g.: `/foo?bar=1`) then the value is treated as single + * value (e.g.: `{ foo: '1' }`). + * + *
    params: {
    +   *     param1: { array: true }
    +   * }
    + * + * - ** squash ** - {bool|string=}: `squash` configures how a default parameter value is represented in the URL when + * the current parameter value is the same as the default value. If `squash` is not set, it uses the + * configured default squash policy. + * (See {@link ui.router.util.$urlMatcherFactory#methods_defaultSquashPolicy `defaultSquashPolicy()`}) + * + * There are three squash settings: + * + * - false: The parameter's default value is not squashed. It is encoded and included in the URL + * - true: The parameter's default value is omitted from the URL. If the parameter is preceeded and followed + * by slashes in the state's `url` declaration, then one of those slashes are omitted. + * This can allow for cleaner looking URLs. + * - `""`: The parameter's default value is replaced with an arbitrary placeholder of your choice. + * + *
    params: {
    +   *     param1: {
    +   *       value: "defaultId",
    +   *       squash: true
    +   * } }
    +   * // squash "defaultValue" to "~"
    +   * params: {
    +   *     param1: {
    +   *       value: "defaultValue",
    +   *       squash: "~"
    +   * } }
    +   * 
    + * + * + * @example + *
    +   * // Some state name examples
    +   *
    +   * // stateName can be a single top-level name (must be unique).
    +   * $stateProvider.state("home", {});
    +   *
    +   * // Or it can be a nested state name. This state is a child of the
    +   * // above "home" state.
    +   * $stateProvider.state("home.newest", {});
    +   *
    +   * // Nest states as deeply as needed.
    +   * $stateProvider.state("home.newest.abc.xyz.inception", {});
    +   *
    +   * // state() returns $stateProvider, so you can chain state declarations.
    +   * $stateProvider
    +   *   .state("home", {})
    +   *   .state("about", {})
    +   *   .state("contacts", {});
    +   * 
    + * + */ + this.state = state; + function state(name, definition) { + /*jshint validthis: true */ + if (isObject(name)) definition = name; + else definition.name = name; + registerState(definition); + return this; + } + + /** + * @ngdoc object + * @name ui.router.state.$state + * + * @requires $rootScope + * @requires $q + * @requires ui.router.state.$view + * @requires $injector + * @requires ui.router.util.$resolve + * @requires ui.router.state.$stateParams + * @requires ui.router.router.$urlRouter + * + * @property {object} params A param object, e.g. {sectionId: section.id)}, that + * you'd like to test against the current active state. + * @property {object} current A reference to the state's config object. However + * you passed it in. Useful for accessing custom data. + * @property {object} transition Currently pending transition. A promise that'll + * resolve or reject. + * + * @description + * `$state` service is responsible for representing states as well as transitioning + * between them. It also provides interfaces to ask for current state or even states + * you're coming from. + */ + this.$get = $get; + $get.$inject = ['$rootScope', '$q', '$view', '$injector', '$resolve', '$stateParams', '$urlRouter', '$location', '$urlMatcherFactory']; + function $get( $rootScope, $q, $view, $injector, $resolve, $stateParams, $urlRouter, $location, $urlMatcherFactory) { + + var TransitionSuperseded = $q.reject(new Error('transition superseded')); + var TransitionPrevented = $q.reject(new Error('transition prevented')); + var TransitionAborted = $q.reject(new Error('transition aborted')); + var TransitionFailed = $q.reject(new Error('transition failed')); + + // Handles the case where a state which is the target of a transition is not found, and the user + // can optionally retry or defer the transition + function handleRedirect(redirect, state, params, options) { + /** + * @ngdoc event + * @name ui.router.state.$state#$stateNotFound + * @eventOf ui.router.state.$state + * @eventType broadcast on root scope + * @description + * Fired when a requested state **cannot be found** using the provided state name during transition. + * The event is broadcast allowing any handlers a single chance to deal with the error (usually by + * lazy-loading the unfound state). A special `unfoundState` object is passed to the listener handler, + * you can see its three properties in the example. You can use `event.preventDefault()` to abort the + * transition and the promise returned from `go` will be rejected with a `'transition aborted'` value. + * + * @param {Object} event Event object. + * @param {Object} unfoundState Unfound State information. Contains: `to, toParams, options` properties. + * @param {State} fromState Current state object. + * @param {Object} fromParams Current state params. + * + * @example + * + *
    +       * // somewhere, assume lazy.state has not been defined
    +       * $state.go("lazy.state", {a:1, b:2}, {inherit:false});
    +       *
    +       * // somewhere else
    +       * $scope.$on('$stateNotFound',
    +       * function(event, unfoundState, fromState, fromParams){
    +       *     console.log(unfoundState.to); // "lazy.state"
    +       *     console.log(unfoundState.toParams); // {a:1, b:2}
    +       *     console.log(unfoundState.options); // {inherit:false} + default options
    +       * })
    +       * 
    + */ + var evt = $rootScope.$broadcast('$stateNotFound', redirect, state, params); + + if (evt.defaultPrevented) { + $urlRouter.update(); + return TransitionAborted; + } + + if (!evt.retry) { + return null; + } + + // Allow the handler to return a promise to defer state lookup retry + if (options.$retry) { + $urlRouter.update(); + return TransitionFailed; + } + var retryTransition = $state.transition = $q.when(evt.retry); + + retryTransition.then(function() { + if (retryTransition !== $state.transition) return TransitionSuperseded; + redirect.options.$retry = true; + return $state.transitionTo(redirect.to, redirect.toParams, redirect.options); + }, function() { + return TransitionAborted; + }); + $urlRouter.update(); + + return retryTransition; + } + + root.locals = { resolve: null, globals: { $stateParams: {} } }; + + $state = { + params: {}, + current: root.self, + $current: root, + transition: null + }; + + /** + * @ngdoc function + * @name ui.router.state.$state#reload + * @methodOf ui.router.state.$state + * + * @description + * A method that force reloads the current state. All resolves are re-resolved, events are not re-fired, + * and controllers reinstantiated (bug with controllers reinstantiating right now, fixing soon). + * + * @example + *
    +     * var app angular.module('app', ['ui.router']);
    +     *
    +     * app.controller('ctrl', function ($scope, $state) {
    +     *   $scope.reload = function(){
    +     *     $state.reload();
    +     *   }
    +     * });
    +     * 
    + * + * `reload()` is just an alias for: + *
    +     * $state.transitionTo($state.current, $stateParams, { 
    +     *   reload: true, inherit: false, notify: true
    +     * });
    +     * 
    + * + * @returns {promise} A promise representing the state of the new transition. See + * {@link ui.router.state.$state#methods_go $state.go}. + */ + $state.reload = function reload() { + return $state.transitionTo($state.current, $stateParams, { reload: true, inherit: false, notify: true }); + }; + + /** + * @ngdoc function + * @name ui.router.state.$state#go + * @methodOf ui.router.state.$state + * + * @description + * Convenience method for transitioning to a new state. `$state.go` calls + * `$state.transitionTo` internally but automatically sets options to + * `{ location: true, inherit: true, relative: $state.$current, notify: true }`. + * This allows you to easily use an absolute or relative to path and specify + * only the parameters you'd like to update (while letting unspecified parameters + * inherit from the currently active ancestor states). + * + * @example + *
    +     * var app = angular.module('app', ['ui.router']);
    +     *
    +     * app.controller('ctrl', function ($scope, $state) {
    +     *   $scope.changeState = function () {
    +     *     $state.go('contact.detail');
    +     *   };
    +     * });
    +     * 
    + * + * + * @param {string} to Absolute state name or relative state path. Some examples: + * + * - `$state.go('contact.detail')` - will go to the `contact.detail` state + * - `$state.go('^')` - will go to a parent state + * - `$state.go('^.sibling')` - will go to a sibling state + * - `$state.go('.child.grandchild')` - will go to grandchild state + * + * @param {object=} params A map of the parameters that will be sent to the state, + * will populate $stateParams. Any parameters that are not specified will be inherited from currently + * defined parameters. This allows, for example, going to a sibling state that shares parameters + * specified in a parent state. Parameter inheritance only works between common ancestor states, I.e. + * transitioning to a sibling will get you the parameters for all parents, transitioning to a child + * will get you all current parameters, etc. + * @param {object=} options Options object. The options are: + * + * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false` + * will not. If string, must be `"replace"`, which will update url and also replace last history record. + * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url. + * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'), + * defines which state to be relative from. + * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events. + * - **`reload`** (v0.2.5) - {boolean=false}, If `true` will force transition even if the state or params + * have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd + * use this when you want to force a reload when *everything* is the same, including search params. + * + * @returns {promise} A promise representing the state of the new transition. + * + * Possible success values: + * + * - $state.current + * + *
    Possible rejection values: + * + * - 'transition superseded' - when a newer transition has been started after this one + * - 'transition prevented' - when `event.preventDefault()` has been called in a `$stateChangeStart` listener + * - 'transition aborted' - when `event.preventDefault()` has been called in a `$stateNotFound` listener or + * when a `$stateNotFound` `event.retry` promise errors. + * - 'transition failed' - when a state has been unsuccessfully found after 2 tries. + * - *resolve error* - when an error has occurred with a `resolve` + * + */ + $state.go = function go(to, params, options) { + return $state.transitionTo(to, params, extend({ inherit: true, relative: $state.$current }, options)); + }; + + /** + * @ngdoc function + * @name ui.router.state.$state#transitionTo + * @methodOf ui.router.state.$state + * + * @description + * Low-level method for transitioning to a new state. {@link ui.router.state.$state#methods_go $state.go} + * uses `transitionTo` internally. `$state.go` is recommended in most situations. + * + * @example + *
    +     * var app = angular.module('app', ['ui.router']);
    +     *
    +     * app.controller('ctrl', function ($scope, $state) {
    +     *   $scope.changeState = function () {
    +     *     $state.transitionTo('contact.detail');
    +     *   };
    +     * });
    +     * 
    + * + * @param {string} to State name. + * @param {object=} toParams A map of the parameters that will be sent to the state, + * will populate $stateParams. + * @param {object=} options Options object. The options are: + * + * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false` + * will not. If string, must be `"replace"`, which will update url and also replace last history record. + * - **`inherit`** - {boolean=false}, If `true` will inherit url parameters from current url. + * - **`relative`** - {object=}, When transitioning with relative path (e.g '^'), + * defines which state to be relative from. + * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events. + * - **`reload`** (v0.2.5) - {boolean=false}, If `true` will force transition even if the state or params + * have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd + * use this when you want to force a reload when *everything* is the same, including search params. + * + * @returns {promise} A promise representing the state of the new transition. See + * {@link ui.router.state.$state#methods_go $state.go}. + */ + $state.transitionTo = function transitionTo(to, toParams, options) { + toParams = toParams || {}; + options = extend({ + location: true, inherit: false, relative: null, notify: true, reload: false, $retry: false + }, options || {}); + + var from = $state.$current, fromParams = $state.params, fromPath = from.path; + var evt, toState = findState(to, options.relative); + + if (!isDefined(toState)) { + var redirect = { to: to, toParams: toParams, options: options }; + var redirectResult = handleRedirect(redirect, from.self, fromParams, options); + + if (redirectResult) { + return redirectResult; + } + + // Always retry once if the $stateNotFound was not prevented + // (handles either redirect changed or state lazy-definition) + to = redirect.to; + toParams = redirect.toParams; + options = redirect.options; + toState = findState(to, options.relative); + + if (!isDefined(toState)) { + if (!options.relative) throw new Error("No such state '" + to + "'"); + throw new Error("Could not resolve '" + to + "' from state '" + options.relative + "'"); + } + } + if (toState[abstractKey]) throw new Error("Cannot transition to abstract state '" + to + "'"); + if (options.inherit) toParams = inheritParams($stateParams, toParams || {}, $state.$current, toState); + if (!toState.params.$$validates(toParams)) return TransitionFailed; + + toParams = toState.params.$$values(toParams); + to = toState; + + var toPath = to.path; + + // Starting from the root of the path, keep all levels that haven't changed + var keep = 0, state = toPath[keep], locals = root.locals, toLocals = []; + + if (!options.reload) { + while (state && state === fromPath[keep] && state.ownParams.$$equals(toParams, fromParams)) { + locals = toLocals[keep] = state.locals; + keep++; + state = toPath[keep]; + } + } + + // If we're going to the same state and all locals are kept, we've got nothing to do. + // But clear 'transition', as we still want to cancel any other pending transitions. + // TODO: We may not want to bump 'transition' if we're called from a location change + // that we've initiated ourselves, because we might accidentally abort a legitimate + // transition initiated from code? + if (shouldTriggerReload(to, from, locals, options)) { + if (to.self.reloadOnSearch !== false) $urlRouter.update(); + $state.transition = null; + return $q.when($state.current); + } + + // Filter parameters before we pass them to event handlers etc. + toParams = filterByKeys(to.params.$$keys(), toParams || {}); + + // Broadcast start event and cancel the transition if requested + if (options.notify) { + /** + * @ngdoc event + * @name ui.router.state.$state#$stateChangeStart + * @eventOf ui.router.state.$state + * @eventType broadcast on root scope + * @description + * Fired when the state transition **begins**. You can use `event.preventDefault()` + * to prevent the transition from happening and then the transition promise will be + * rejected with a `'transition prevented'` value. + * + * @param {Object} event Event object. + * @param {State} toState The state being transitioned to. + * @param {Object} toParams The params supplied to the `toState`. + * @param {State} fromState The current state, pre-transition. + * @param {Object} fromParams The params supplied to the `fromState`. + * + * @example + * + *
    +         * $rootScope.$on('$stateChangeStart',
    +         * function(event, toState, toParams, fromState, fromParams){
    +         *     event.preventDefault();
    +         *     // transitionTo() promise will be rejected with
    +         *     // a 'transition prevented' error
    +         * })
    +         * 
    + */ + if ($rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams).defaultPrevented) { + $urlRouter.update(); + return TransitionPrevented; + } + } + + // Resolve locals for the remaining states, but don't update any global state just + // yet -- if anything fails to resolve the current state needs to remain untouched. + // We also set up an inheritance chain for the locals here. This allows the view directive + // to quickly look up the correct definition for each view in the current state. Even + // though we create the locals object itself outside resolveState(), it is initially + // empty and gets filled asynchronously. We need to keep track of the promise for the + // (fully resolved) current locals, and pass this down the chain. + var resolved = $q.when(locals); + + for (var l = keep; l < toPath.length; l++, state = toPath[l]) { + locals = toLocals[l] = inherit(locals); + resolved = resolveState(state, toParams, state === to, resolved, locals, options); + } + + // Once everything is resolved, we are ready to perform the actual transition + // and return a promise for the new state. We also keep track of what the + // current promise is, so that we can detect overlapping transitions and + // keep only the outcome of the last transition. + var transition = $state.transition = resolved.then(function () { + var l, entering, exiting; + + if ($state.transition !== transition) return TransitionSuperseded; + + // Exit 'from' states not kept + for (l = fromPath.length - 1; l >= keep; l--) { + exiting = fromPath[l]; + if (exiting.self.onExit) { + $injector.invoke(exiting.self.onExit, exiting.self, exiting.locals.globals); + } + exiting.locals = null; + } + + // Enter 'to' states not kept + for (l = keep; l < toPath.length; l++) { + entering = toPath[l]; + entering.locals = toLocals[l]; + if (entering.self.onEnter) { + $injector.invoke(entering.self.onEnter, entering.self, entering.locals.globals); + } + } + + // Run it again, to catch any transitions in callbacks + if ($state.transition !== transition) return TransitionSuperseded; + + // Update globals in $state + $state.$current = to; + $state.current = to.self; + $state.params = toParams; + copy($state.params, $stateParams); + $state.transition = null; + + if (options.location && to.navigable) { + $urlRouter.push(to.navigable.url, to.navigable.locals.globals.$stateParams, { + $$avoidResync: true, replace: options.location === 'replace' + }); + } + + if (options.notify) { + /** + * @ngdoc event + * @name ui.router.state.$state#$stateChangeSuccess + * @eventOf ui.router.state.$state + * @eventType broadcast on root scope + * @description + * Fired once the state transition is **complete**. + * + * @param {Object} event Event object. + * @param {State} toState The state being transitioned to. + * @param {Object} toParams The params supplied to the `toState`. + * @param {State} fromState The current state, pre-transition. + * @param {Object} fromParams The params supplied to the `fromState`. + */ + $rootScope.$broadcast('$stateChangeSuccess', to.self, toParams, from.self, fromParams); + } + $urlRouter.update(true); + + return $state.current; + }, function (error) { + if ($state.transition !== transition) return TransitionSuperseded; + + $state.transition = null; + /** + * @ngdoc event + * @name ui.router.state.$state#$stateChangeError + * @eventOf ui.router.state.$state + * @eventType broadcast on root scope + * @description + * Fired when an **error occurs** during transition. It's important to note that if you + * have any errors in your resolve functions (javascript errors, non-existent services, etc) + * they will not throw traditionally. You must listen for this $stateChangeError event to + * catch **ALL** errors. + * + * @param {Object} event Event object. + * @param {State} toState The state being transitioned to. + * @param {Object} toParams The params supplied to the `toState`. + * @param {State} fromState The current state, pre-transition. + * @param {Object} fromParams The params supplied to the `fromState`. + * @param {Error} error The resolve error object. + */ + evt = $rootScope.$broadcast('$stateChangeError', to.self, toParams, from.self, fromParams, error); + + if (!evt.defaultPrevented) { + $urlRouter.update(); + } + + return $q.reject(error); + }); + + return transition; + }; + + /** + * @ngdoc function + * @name ui.router.state.$state#is + * @methodOf ui.router.state.$state + * + * @description + * Similar to {@link ui.router.state.$state#methods_includes $state.includes}, + * but only checks for the full state name. If params is supplied then it will be + * tested for strict equality against the current active params object, so all params + * must match with none missing and no extras. + * + * @example + *
    +     * $state.$current.name = 'contacts.details.item';
    +     *
    +     * // absolute name
    +     * $state.is('contact.details.item'); // returns true
    +     * $state.is(contactDetailItemStateObject); // returns true
    +     *
    +     * // relative name (. and ^), typically from a template
    +     * // E.g. from the 'contacts.details' template
    +     * 
    Item
    + *
    + * + * @param {string|object} stateOrName The state name (absolute or relative) or state object you'd like to check. + * @param {object=} params A param object, e.g. `{sectionId: section.id}`, that you'd like + * to test against the current active state. + * @param {object=} options An options object. The options are: + * + * - **`relative`** - {string|object} - If `stateOrName` is a relative state name and `options.relative` is set, .is will + * test relative to `options.relative` state (or name). + * + * @returns {boolean} Returns true if it is the state. + */ + $state.is = function is(stateOrName, params, options) { + options = extend({ relative: $state.$current }, options || {}); + var state = findState(stateOrName, options.relative); + + if (!isDefined(state)) { return undefined; } + if ($state.$current !== state) { return false; } + return params ? equalForKeys(state.params.$$values(params), $stateParams) : true; + }; + + /** + * @ngdoc function + * @name ui.router.state.$state#includes + * @methodOf ui.router.state.$state + * + * @description + * A method to determine if the current active state is equal to or is the child of the + * state stateName. If any params are passed then they will be tested for a match as well. + * Not all the parameters need to be passed, just the ones you'd like to test for equality. + * + * @example + * Partial and relative names + *
    +     * $state.$current.name = 'contacts.details.item';
    +     *
    +     * // Using partial names
    +     * $state.includes("contacts"); // returns true
    +     * $state.includes("contacts.details"); // returns true
    +     * $state.includes("contacts.details.item"); // returns true
    +     * $state.includes("contacts.list"); // returns false
    +     * $state.includes("about"); // returns false
    +     *
    +     * // Using relative names (. and ^), typically from a template
    +     * // E.g. from the 'contacts.details' template
    +     * 
    Item
    + *
    + * + * Basic globbing patterns + *
    +     * $state.$current.name = 'contacts.details.item.url';
    +     *
    +     * $state.includes("*.details.*.*"); // returns true
    +     * $state.includes("*.details.**"); // returns true
    +     * $state.includes("**.item.**"); // returns true
    +     * $state.includes("*.details.item.url"); // returns true
    +     * $state.includes("*.details.*.url"); // returns true
    +     * $state.includes("*.details.*"); // returns false
    +     * $state.includes("item.**"); // returns false
    +     * 
    + * + * @param {string} stateOrName A partial name, relative name, or glob pattern + * to be searched for within the current state name. + * @param {object=} params A param object, e.g. `{sectionId: section.id}`, + * that you'd like to test against the current active state. + * @param {object=} options An options object. The options are: + * + * - **`relative`** - {string|object=} - If `stateOrName` is a relative state reference and `options.relative` is set, + * .includes will test relative to `options.relative` state (or name). + * + * @returns {boolean} Returns true if it does include the state + */ + $state.includes = function includes(stateOrName, params, options) { + options = extend({ relative: $state.$current }, options || {}); + if (isString(stateOrName) && isGlob(stateOrName)) { + if (!doesStateMatchGlob(stateOrName)) { + return false; + } + stateOrName = $state.$current.name; + } + + var state = findState(stateOrName, options.relative); + if (!isDefined(state)) { return undefined; } + if (!isDefined($state.$current.includes[state.name])) { return false; } + return params ? equalForKeys(state.params.$$values(params), $stateParams, objectKeys(params)) : true; + }; + + + /** + * @ngdoc function + * @name ui.router.state.$state#href + * @methodOf ui.router.state.$state + * + * @description + * A url generation method that returns the compiled url for the given state populated with the given params. + * + * @example + *
    +     * expect($state.href("about.person", { person: "bob" })).toEqual("/about/bob");
    +     * 
    + * + * @param {string|object} stateOrName The state name or state object you'd like to generate a url from. + * @param {object=} params An object of parameter values to fill the state's required parameters. + * @param {object=} options Options object. The options are: + * + * - **`lossy`** - {boolean=true} - If true, and if there is no url associated with the state provided in the + * first parameter, then the constructed href url will be built from the first navigable ancestor (aka + * ancestor with a valid url). + * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url. + * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'), + * defines which state to be relative from. + * - **`absolute`** - {boolean=false}, If true will generate an absolute url, e.g. "http://www.example.com/fullurl". + * + * @returns {string} compiled state url + */ + $state.href = function href(stateOrName, params, options) { + options = extend({ + lossy: true, + inherit: true, + absolute: false, + relative: $state.$current + }, options || {}); + + var state = findState(stateOrName, options.relative); + + if (!isDefined(state)) return null; + if (options.inherit) params = inheritParams($stateParams, params || {}, $state.$current, state); + + var nav = (state && options.lossy) ? state.navigable : state; + + if (!nav || nav.url === undefined || nav.url === null) { + return null; + } + return $urlRouter.href(nav.url, filterByKeys(state.params.$$keys(), params || {}), { + absolute: options.absolute + }); + }; + + /** + * @ngdoc function + * @name ui.router.state.$state#get + * @methodOf ui.router.state.$state + * + * @description + * Returns the state configuration object for any specific state or all states. + * + * @param {string|object=} stateOrName (absolute or relative) If provided, will only get the config for + * the requested state. If not provided, returns an array of ALL state configs. + * @param {string|object=} context When stateOrName is a relative state reference, the state will be retrieved relative to context. + * @returns {Object|Array} State configuration object or array of all objects. + */ + $state.get = function (stateOrName, context) { + if (arguments.length === 0) return map(objectKeys(states), function(name) { return states[name].self; }); + var state = findState(stateOrName, context || $state.$current); + return (state && state.self) ? state.self : null; + }; + + function resolveState(state, params, paramsAreFiltered, inherited, dst, options) { + // Make a restricted $stateParams with only the parameters that apply to this state if + // necessary. In addition to being available to the controller and onEnter/onExit callbacks, + // we also need $stateParams to be available for any $injector calls we make during the + // dependency resolution process. + var $stateParams = (paramsAreFiltered) ? params : filterByKeys(state.params.$$keys(), params); + var locals = { $stateParams: $stateParams }; + + // Resolve 'global' dependencies for the state, i.e. those not specific to a view. + // We're also including $stateParams in this; that way the parameters are restricted + // to the set that should be visible to the state, and are independent of when we update + // the global $state and $stateParams values. + dst.resolve = $resolve.resolve(state.resolve, locals, dst.resolve, state); + var promises = [dst.resolve.then(function (globals) { + dst.globals = globals; + })]; + if (inherited) promises.push(inherited); + + // Resolve template and dependencies for all views. + forEach(state.views, function (view, name) { + var injectables = (view.resolve && view.resolve !== state.resolve ? view.resolve : {}); + injectables.$template = [ function () { + return $view.load(name, { view: view, locals: locals, params: $stateParams, notify: options.notify }) || ''; + }]; + + promises.push($resolve.resolve(injectables, locals, dst.resolve, state).then(function (result) { + // References to the controller (only instantiated at link time) + if (isFunction(view.controllerProvider) || isArray(view.controllerProvider)) { + var injectLocals = angular.extend({}, injectables, locals); + result.$$controller = $injector.invoke(view.controllerProvider, null, injectLocals); + } else { + result.$$controller = view.controller; + } + // Provide access to the state itself for internal use + result.$$state = state; + result.$$controllerAs = view.controllerAs; + dst[name] = result; + })); + }); + + // Wait for all the promises and then return the activation object + return $q.all(promises).then(function (values) { + return dst; + }); + } + + return $state; + } + + function shouldTriggerReload(to, from, locals, options) { + if (to === from && ((locals === from.locals && !options.reload) || (to.self.reloadOnSearch === false))) { + return true; + } + } +} + +angular.module('ui.router.state') + .value('$stateParams', {}) + .provider('$state', $StateProvider); + + +$ViewProvider.$inject = []; +function $ViewProvider() { + + this.$get = $get; + /** + * @ngdoc object + * @name ui.router.state.$view + * + * @requires ui.router.util.$templateFactory + * @requires $rootScope + * + * @description + * + */ + $get.$inject = ['$rootScope', '$templateFactory']; + function $get( $rootScope, $templateFactory) { + return { + // $view.load('full.viewName', { template: ..., controller: ..., resolve: ..., async: false, params: ... }) + /** + * @ngdoc function + * @name ui.router.state.$view#load + * @methodOf ui.router.state.$view + * + * @description + * + * @param {string} name name + * @param {object} options option object. + */ + load: function load(name, options) { + var result, defaults = { + template: null, controller: null, view: null, locals: null, notify: true, async: true, params: {} + }; + options = extend(defaults, options); + + if (options.view) { + result = $templateFactory.fromConfig(options.view, options.params, options.locals); + } + if (result && options.notify) { + /** + * @ngdoc event + * @name ui.router.state.$state#$viewContentLoading + * @eventOf ui.router.state.$view + * @eventType broadcast on root scope + * @description + * + * Fired once the view **begins loading**, *before* the DOM is rendered. + * + * @param {Object} event Event object. + * @param {Object} viewConfig The view config properties (template, controller, etc). + * + * @example + * + *
    +         * $scope.$on('$viewContentLoading',
    +         * function(event, viewConfig){
    +         *     // Access to all the view config properties.
    +         *     // and one special property 'targetView'
    +         *     // viewConfig.targetView
    +         * });
    +         * 
    + */ + $rootScope.$broadcast('$viewContentLoading', options); + } + return result; + } + }; + } +} + +angular.module('ui.router.state').provider('$view', $ViewProvider); + +/** + * @ngdoc object + * @name ui.router.state.$uiViewScrollProvider + * + * @description + * Provider that returns the {@link ui.router.state.$uiViewScroll} service function. + */ +function $ViewScrollProvider() { + + var useAnchorScroll = false; + + /** + * @ngdoc function + * @name ui.router.state.$uiViewScrollProvider#useAnchorScroll + * @methodOf ui.router.state.$uiViewScrollProvider + * + * @description + * Reverts back to using the core [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll) service for + * scrolling based on the url anchor. + */ + this.useAnchorScroll = function () { + useAnchorScroll = true; + }; + + /** + * @ngdoc object + * @name ui.router.state.$uiViewScroll + * + * @requires $anchorScroll + * @requires $timeout + * + * @description + * When called with a jqLite element, it scrolls the element into view (after a + * `$timeout` so the DOM has time to refresh). + * + * If you prefer to rely on `$anchorScroll` to scroll the view to the anchor, + * this can be enabled by calling {@link ui.router.state.$uiViewScrollProvider#methods_useAnchorScroll `$uiViewScrollProvider.useAnchorScroll()`}. + */ + this.$get = ['$anchorScroll', '$timeout', function ($anchorScroll, $timeout) { + if (useAnchorScroll) { + return $anchorScroll; + } + + return function ($element) { + $timeout(function () { + $element[0].scrollIntoView(); + }, 0, false); + }; + }]; +} + +angular.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider); + +/** + * @ngdoc directive + * @name ui.router.state.directive:ui-view + * + * @requires ui.router.state.$state + * @requires $compile + * @requires $controller + * @requires $injector + * @requires ui.router.state.$uiViewScroll + * @requires $document + * + * @restrict ECA + * + * @description + * The ui-view directive tells $state where to place your templates. + * + * @param {string=} name A view name. The name should be unique amongst the other views in the + * same state. You can have views of the same name that live in different states. + * + * @param {string=} autoscroll It allows you to set the scroll behavior of the browser window + * when a view is populated. By default, $anchorScroll is overridden by ui-router's custom scroll + * service, {@link ui.router.state.$uiViewScroll}. This custom service let's you + * scroll ui-view elements into view when they are populated during a state activation. + * + * *Note: To revert back to old [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll) + * functionality, call `$uiViewScrollProvider.useAnchorScroll()`.* + * + * @param {string=} onload Expression to evaluate whenever the view updates. + * + * @example + * A view can be unnamed or named. + *
    + * 
    + * 
    + * + * + *
    + *
    + * + * You can only have one unnamed view within any template (or root html). If you are only using a + * single view and it is unnamed then you can populate it like so: + *
    + * 
    + * $stateProvider.state("home", { + * template: "

    HELLO!

    " + * }) + *
    + * + * The above is a convenient shortcut equivalent to specifying your view explicitly with the {@link ui.router.state.$stateProvider#views `views`} + * config property, by name, in this case an empty name: + *
    + * $stateProvider.state("home", {
    + *   views: {
    + *     "": {
    + *       template: "

    HELLO!

    " + * } + * } + * }) + *
    + * + * But typically you'll only use the views property if you name your view or have more than one view + * in the same template. There's not really a compelling reason to name a view if its the only one, + * but you could if you wanted, like so: + *
    + * 
    + *
    + *
    + * $stateProvider.state("home", {
    + *   views: {
    + *     "main": {
    + *       template: "

    HELLO!

    " + * } + * } + * }) + *
    + * + * Really though, you'll use views to set up multiple views: + *
    + * 
    + *
    + *
    + *
    + * + *
    + * $stateProvider.state("home", {
    + *   views: {
    + *     "": {
    + *       template: "

    HELLO!

    " + * }, + * "chart": { + * template: "" + * }, + * "data": { + * template: "" + * } + * } + * }) + *
    + * + * Examples for `autoscroll`: + * + *
    + * 
    + * 
    + *
    + * 
    + * 
    + * 
    + * 
    + * 
    + */ +$ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate']; +function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate) { + + function getService() { + return ($injector.has) ? function(service) { + return $injector.has(service) ? $injector.get(service) : null; + } : function(service) { + try { + return $injector.get(service); + } catch (e) { + return null; + } + }; + } + + var service = getService(), + $animator = service('$animator'), + $animate = service('$animate'); + + // Returns a set of DOM manipulation functions based on which Angular version + // it should use + function getRenderer(attrs, scope) { + var statics = function() { + return { + enter: function (element, target, cb) { target.after(element); cb(); }, + leave: function (element, cb) { element.remove(); cb(); } + }; + }; + + if ($animate) { + return { + enter: function(element, target, cb) { + var promise = $animate.enter(element, null, target, cb); + if (promise && promise.then) promise.then(cb); + }, + leave: function(element, cb) { + var promise = $animate.leave(element, cb); + if (promise && promise.then) promise.then(cb); + } + }; + } + + if ($animator) { + var animate = $animator && $animator(scope, attrs); + + return { + enter: function(element, target, cb) {animate.enter(element, null, target); cb(); }, + leave: function(element, cb) { animate.leave(element); cb(); } + }; + } + + return statics(); + } + + var directive = { + restrict: 'ECA', + terminal: true, + priority: 400, + transclude: 'element', + compile: function (tElement, tAttrs, $transclude) { + return function (scope, $element, attrs) { + var previousEl, currentEl, currentScope, latestLocals, + onloadExp = attrs.onload || '', + autoScrollExp = attrs.autoscroll, + renderer = getRenderer(attrs, scope); + + scope.$on('$stateChangeSuccess', function() { + updateView(false); + }); + scope.$on('$viewContentLoading', function() { + updateView(false); + }); + + updateView(true); + + function cleanupLastView() { + if (previousEl) { + previousEl.remove(); + previousEl = null; + } + + if (currentScope) { + currentScope.$destroy(); + currentScope = null; + } + + if (currentEl) { + renderer.leave(currentEl, function() { + previousEl = null; + }); + + previousEl = currentEl; + currentEl = null; + } + } + + function updateView(firstTime) { + var newScope, + name = getUiViewName(scope, attrs, $element, $interpolate), + previousLocals = name && $state.$current && $state.$current.locals[name]; + + if (!firstTime && previousLocals === latestLocals) return; // nothing to do + newScope = scope.$new(); + latestLocals = $state.$current.locals[name]; + + var clone = $transclude(newScope, function(clone) { + renderer.enter(clone, $element, function onUiViewEnter() { + if(currentScope) { + currentScope.$emit('$viewContentAnimationEnded'); + } + + if (angular.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) { + $uiViewScroll(clone); + } + }); + cleanupLastView(); + }); + + currentEl = clone; + currentScope = newScope; + /** + * @ngdoc event + * @name ui.router.state.directive:ui-view#$viewContentLoaded + * @eventOf ui.router.state.directive:ui-view + * @eventType emits on ui-view directive scope + * @description * + * Fired once the view is **loaded**, *after* the DOM is rendered. + * + * @param {Object} event Event object. + */ + currentScope.$emit('$viewContentLoaded'); + currentScope.$eval(onloadExp); + } + }; + } + }; + + return directive; +} + +$ViewDirectiveFill.$inject = ['$compile', '$controller', '$state', '$interpolate']; +function $ViewDirectiveFill ( $compile, $controller, $state, $interpolate) { + return { + restrict: 'ECA', + priority: -400, + compile: function (tElement) { + var initial = tElement.html(); + return function (scope, $element, attrs) { + var current = $state.$current, + name = getUiViewName(scope, attrs, $element, $interpolate), + locals = current && current.locals[name]; + + if (! locals) { + return; + } + + $element.data('$uiView', { name: name, state: locals.$$state }); + $element.html(locals.$template ? locals.$template : initial); + + var link = $compile($element.contents()); + + if (locals.$$controller) { + locals.$scope = scope; + var controller = $controller(locals.$$controller, locals); + if (locals.$$controllerAs) { + scope[locals.$$controllerAs] = controller; + } + $element.data('$ngControllerController', controller); + $element.children().data('$ngControllerController', controller); + } + + link(scope); + }; + } + }; +} + +/** + * Shared ui-view code for both directives: + * Given scope, element, and its attributes, return the view's name + */ +function getUiViewName(scope, attrs, element, $interpolate) { + var name = $interpolate(attrs.uiView || attrs.name || '')(scope); + var inherited = element.inheritedData('$uiView'); + return name.indexOf('@') >= 0 ? name : (name + '@' + (inherited ? inherited.state.name : '')); +} + +angular.module('ui.router.state').directive('uiView', $ViewDirective); +angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill); + +function parseStateRef(ref, current) { + var preparsed = ref.match(/^\s*({[^}]*})\s*$/), parsed; + if (preparsed) ref = current + '(' + preparsed[1] + ')'; + parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/); + if (!parsed || parsed.length !== 4) throw new Error("Invalid state ref '" + ref + "'"); + return { state: parsed[1], paramExpr: parsed[3] || null }; +} + +function stateContext(el) { + var stateData = el.parent().inheritedData('$uiView'); + + if (stateData && stateData.state && stateData.state.name) { + return stateData.state; + } +} + +/** + * @ngdoc directive + * @name ui.router.state.directive:ui-sref + * + * @requires ui.router.state.$state + * @requires $timeout + * + * @restrict A + * + * @description + * A directive that binds a link (`` tag) to a state. If the state has an associated + * URL, the directive will automatically generate & update the `href` attribute via + * the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking + * the link will trigger a state transition with optional parameters. + * + * Also middle-clicking, right-clicking, and ctrl-clicking on the link will be + * handled natively by the browser. + * + * You can also use relative state paths within ui-sref, just like the relative + * paths passed to `$state.go()`. You just need to be aware that the path is relative + * to the state that the link lives in, in other words the state that loaded the + * template containing the link. + * + * You can specify options to pass to {@link ui.router.state.$state#go $state.go()} + * using the `ui-sref-opts` attribute. Options are restricted to `location`, `inherit`, + * and `reload`. + * + * @example + * Here's an example of how you'd use ui-sref and how it would compile. If you have the + * following template: + *
    + * Home | About | Next page
    + * 
    + * 
    + * 
    + * + * Then the compiled html would be (assuming Html5Mode is off and current state is contacts): + *
    + * Home | About | Next page
    + * 
    + * 
      + *
    • + * Joe + *
    • + *
    • + * Alice + *
    • + *
    • + * Bob + *
    • + *
    + * + * Home + *
    + * + * @param {string} ui-sref 'stateName' can be any valid absolute or relative state + * @param {Object} ui-sref-opts options to pass to {@link ui.router.state.$state#go $state.go()} + */ +$StateRefDirective.$inject = ['$state', '$timeout']; +function $StateRefDirective($state, $timeout) { + var allowedOptions = ['location', 'inherit', 'reload']; + + return { + restrict: 'A', + require: ['?^uiSrefActive', '?^uiSrefActiveEq'], + link: function(scope, element, attrs, uiSrefActive) { + var ref = parseStateRef(attrs.uiSref, $state.current.name); + var params = null, url = null, base = stateContext(element) || $state.$current; + var newHref = null, isAnchor = element.prop("tagName") === "A"; + var isForm = element[0].nodeName === "FORM"; + var attr = isForm ? "action" : "href", nav = true; + + var options = { relative: base, inherit: true }; + var optionsOverride = scope.$eval(attrs.uiSrefOpts) || {}; + + angular.forEach(allowedOptions, function(option) { + if (option in optionsOverride) { + options[option] = optionsOverride[option]; + } + }); + + var update = function(newVal) { + if (newVal) params = angular.copy(newVal); + if (!nav) return; + + newHref = $state.href(ref.state, params, options); + + var activeDirective = uiSrefActive[1] || uiSrefActive[0]; + if (activeDirective) { + activeDirective.$$setStateInfo(ref.state, params); + } + if (newHref === null) { + nav = false; + return false; + } + attrs.$set(attr, newHref); + }; + + if (ref.paramExpr) { + scope.$watch(ref.paramExpr, function(newVal, oldVal) { + if (newVal !== params) update(newVal); + }, true); + params = angular.copy(scope.$eval(ref.paramExpr)); + } + update(); + + if (isForm) return; + + element.bind("click", function(e) { + var button = e.which || e.button; + if ( !(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || element.attr('target')) ) { + // HACK: This is to allow ng-clicks to be processed before the transition is initiated: + var transition = $timeout(function() { + $state.go(ref.state, params, options); + }); + e.preventDefault(); + + // if the state has no URL, ignore one preventDefault from the directive. + var ignorePreventDefaultCount = isAnchor && !newHref ? 1: 0; + e.preventDefault = function() { + if (ignorePreventDefaultCount-- <= 0) + $timeout.cancel(transition); + }; + } + }); + } + }; +} + +/** + * @ngdoc directive + * @name ui.router.state.directive:ui-sref-active + * + * @requires ui.router.state.$state + * @requires ui.router.state.$stateParams + * @requires $interpolate + * + * @restrict A + * + * @description + * A directive working alongside ui-sref to add classes to an element when the + * related ui-sref directive's state is active, and removing them when it is inactive. + * The primary use-case is to simplify the special appearance of navigation menus + * relying on `ui-sref`, by having the "active" state's menu button appear different, + * distinguishing it from the inactive menu items. + * + * ui-sref-active can live on the same element as ui-sref or on a parent element. The first + * ui-sref-active found at the same level or above the ui-sref will be used. + * + * Will activate when the ui-sref's target state or any child state is active. If you + * need to activate only when the ui-sref target state is active and *not* any of + * it's children, then you will use + * {@link ui.router.state.directive:ui-sref-active-eq ui-sref-active-eq} + * + * @example + * Given the following template: + *
    + * 
    + * 
    + * + * + * When the app state is "app.user" (or any children states), and contains the state parameter "user" with value "bilbobaggins", + * the resulting HTML will appear as (note the 'active' class): + *
    + * 
    + * 
    + * + * The class name is interpolated **once** during the directives link time (any further changes to the + * interpolated value are ignored). + * + * Multiple classes may be specified in a space-separated format: + *
    + * 
      + *
    • + * link + *
    • + *
    + *
    + */ + +/** + * @ngdoc directive + * @name ui.router.state.directive:ui-sref-active-eq + * + * @requires ui.router.state.$state + * @requires ui.router.state.$stateParams + * @requires $interpolate + * + * @restrict A + * + * @description + * The same as {@link ui.router.state.directive:ui-sref-active ui-sref-active} but will only activate + * when the exact target state used in the `ui-sref` is active; no child states. + * + */ +$StateRefActiveDirective.$inject = ['$state', '$stateParams', '$interpolate']; +function $StateRefActiveDirective($state, $stateParams, $interpolate) { + return { + restrict: "A", + controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) { + var state, params, activeClass; + + // There probably isn't much point in $observing this + // uiSrefActive and uiSrefActiveEq share the same directive object with some + // slight difference in logic routing + activeClass = $interpolate($attrs.uiSrefActiveEq || $attrs.uiSrefActive || '', false)($scope); + + // Allow uiSref to communicate with uiSrefActive[Equals] + this.$$setStateInfo = function (newState, newParams) { + state = $state.get(newState, stateContext($element)); + params = newParams; + update(); + }; + + $scope.$on('$stateChangeSuccess', update); + + // Update route state + function update() { + if (isMatch()) { + $element.addClass(activeClass); + } else { + $element.removeClass(activeClass); + } + } + + function isMatch() { + if (typeof $attrs.uiSrefActiveEq !== 'undefined') { + return state && $state.is(state.name, params); + } else { + return state && $state.includes(state.name, params); + } + } + }] + }; +} + +angular.module('ui.router.state') + .directive('uiSref', $StateRefDirective) + .directive('uiSrefActive', $StateRefActiveDirective) + .directive('uiSrefActiveEq', $StateRefActiveDirective); + +/** + * @ngdoc filter + * @name ui.router.state.filter:isState + * + * @requires ui.router.state.$state + * + * @description + * Translates to {@link ui.router.state.$state#methods_is $state.is("stateName")}. + */ +$IsStateFilter.$inject = ['$state']; +function $IsStateFilter($state) { + var isFilter = function (state) { + return $state.is(state); + }; + isFilter.$stateful = true; + return isFilter; +} + +/** + * @ngdoc filter + * @name ui.router.state.filter:includedByState + * + * @requires ui.router.state.$state + * + * @description + * Translates to {@link ui.router.state.$state#methods_includes $state.includes('fullOrPartialStateName')}. + */ +$IncludedByStateFilter.$inject = ['$state']; +function $IncludedByStateFilter($state) { + var includesFilter = function (state) { + return $state.includes(state); + }; + includesFilter.$stateful = true; + return includesFilter; +} + +angular.module('ui.router.state') + .filter('isState', $IsStateFilter) + .filter('includedByState', $IncludedByStateFilter); +})(window, window.angular); \ No newline at end of file diff --git a/app/www/lib/ionic/js/angular-ui/angular-ui-router.min.js b/app/www/lib/ionic/js/angular-ui/angular-ui-router.min.js new file mode 100644 index 0000000..be06fb5 --- /dev/null +++ b/app/www/lib/ionic/js/angular-ui/angular-ui-router.min.js @@ -0,0 +1,7 @@ +/** + * State-based routing for AngularJS + * @version v0.2.13 + * @link http://angular-ui.github.com/ + * @license MIT License, http://www.opensource.org/licenses/MIT + */ +"undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="ui.router"),function(a,b,c){"use strict";function d(a,b){return M(new(M(function(){},{prototype:a})),b)}function e(a){return L(arguments,function(b){b!==a&&L(b,function(b,c){a.hasOwnProperty(c)||(a[c]=b)})}),a}function f(a,b){var c=[];for(var d in a.path){if(a.path[d]!==b.path[d])break;c.push(a.path[d])}return c}function g(a){if(Object.keys)return Object.keys(a);var c=[];return b.forEach(a,function(a,b){c.push(b)}),c}function h(a,b){if(Array.prototype.indexOf)return a.indexOf(b,Number(arguments[2])||0);var c=a.length>>>0,d=Number(arguments[2])||0;for(d=0>d?Math.ceil(d):Math.floor(d),0>d&&(d+=c);c>d;d++)if(d in a&&a[d]===b)return d;return-1}function i(a,b,c,d){var e,i=f(c,d),j={},k=[];for(var l in i)if(i[l].params&&(e=g(i[l].params),e.length))for(var m in e)h(k,e[m])>=0||(k.push(e[m]),j[e[m]]=a[e[m]]);return M({},j,b)}function j(a,b,c){if(!c){c=[];for(var d in a)c.push(d)}for(var e=0;e "));if(s[c]=d,I(a))q.push(c,[function(){return b.get(a)}],j);else{var e=b.annotate(a);L(e,function(a){a!==c&&i.hasOwnProperty(a)&&n(i[a],a)}),q.push(c,a,e)}r.pop(),s[c]=f}}function o(a){return J(a)&&a.then&&a.$$promises}if(!J(i))throw new Error("'invocables' must be an object");var p=g(i||{}),q=[],r=[],s={};return L(i,n),i=r=s=null,function(d,f,g){function h(){--u||(v||e(t,f.$$values),r.$$values=t,r.$$promises=r.$$promises||!0,delete r.$$inheritedValues,n.resolve(t))}function i(a){r.$$failure=a,n.reject(a)}function j(c,e,f){function j(a){l.reject(a),i(a)}function k(){if(!G(r.$$failure))try{l.resolve(b.invoke(e,g,t)),l.promise.then(function(a){t[c]=a,h()},j)}catch(a){j(a)}}var l=a.defer(),m=0;L(f,function(a){s.hasOwnProperty(a)&&!d.hasOwnProperty(a)&&(m++,s[a].then(function(b){t[a]=b,--m||k()},j))}),m||k(),s[c]=l.promise}if(o(d)&&g===c&&(g=f,f=d,d=null),d){if(!J(d))throw new Error("'locals' must be an object")}else d=k;if(f){if(!o(f))throw new Error("'parent' must be a promise returned by $resolve.resolve()")}else f=m;var n=a.defer(),r=n.promise,s=r.$$promises={},t=M({},d),u=1+q.length/3,v=!1;if(G(f.$$failure))return i(f.$$failure),r;f.$$inheritedValues&&e(t,l(f.$$inheritedValues,p)),M(s,f.$$promises),f.$$values?(v=e(t,l(f.$$values,p)),r.$$inheritedValues=l(f.$$values,p),h()):(f.$$inheritedValues&&(r.$$inheritedValues=l(f.$$inheritedValues,p)),f.then(h,i));for(var w=0,x=q.length;x>w;w+=3)d.hasOwnProperty(q[w])?h():j(q[w],q[w+1],q[w+2]);return r}},this.resolve=function(a,b,c,d){return this.study(a)(b,c,d)}}function p(a,b,c){this.fromConfig=function(a,b,c){return G(a.template)?this.fromString(a.template,b):G(a.templateUrl)?this.fromUrl(a.templateUrl,b):G(a.templateProvider)?this.fromProvider(a.templateProvider,b,c):null},this.fromString=function(a,b){return H(a)?a(b):a},this.fromUrl=function(c,d){return H(c)&&(c=c(d)),null==c?null:a.get(c,{cache:b,headers:{Accept:"text/html"}}).then(function(a){return a.data})},this.fromProvider=function(a,b,d){return c.invoke(a,null,d||{params:b})}}function q(a,b,e){function f(b,c,d,e){if(q.push(b),o[b])return o[b];if(!/^\w+(-+\w+)*(?:\[\])?$/.test(b))throw new Error("Invalid parameter name '"+b+"' in pattern '"+a+"'");if(p[b])throw new Error("Duplicate parameter name '"+b+"' in pattern '"+a+"'");return p[b]=new O.Param(b,c,d,e),p[b]}function g(a,b,c){var d=["",""],e=a.replace(/[\\\[\]\^$*+?.()|{}]/g,"\\$&");if(!b)return e;switch(c){case!1:d=["(",")"];break;case!0:d=["?(",")?"];break;default:d=["("+c+"|",")?"]}return e+d[0]+b+d[1]}function h(c,e){var f,g,h,i,j;return f=c[2]||c[3],j=b.params[f],h=a.substring(m,c.index),g=e?c[4]:c[4]||("*"==c[1]?".*":null),i=O.type(g||"string")||d(O.type("string"),{pattern:new RegExp(g)}),{id:f,regexp:g,segment:h,type:i,cfg:j}}b=M({params:{}},J(b)?b:{});var i,j=/([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,k=/([:]?)([\w\[\]-]+)|\{([\w\[\]-]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,l="^",m=0,n=this.segments=[],o=e?e.params:{},p=this.params=e?e.params.$$new():new O.ParamSet,q=[];this.source=a;for(var r,s,t;(i=j.exec(a))&&(r=h(i,!1),!(r.segment.indexOf("?")>=0));)s=f(r.id,r.type,r.cfg,"path"),l+=g(r.segment,s.type.pattern.source,s.squash),n.push(r.segment),m=j.lastIndex;t=a.substring(m);var u=t.indexOf("?");if(u>=0){var v=this.sourceSearch=t.substring(u);if(t=t.substring(0,u),this.sourcePath=a.substring(0,m+u),v.length>0)for(m=0;i=k.exec(v);)r=h(i,!0),s=f(r.id,r.type,r.cfg,"search"),m=j.lastIndex}else this.sourcePath=a,this.sourceSearch="";l+=g(t)+(b.strict===!1?"/?":"")+"$",n.push(t),this.regexp=new RegExp(l,b.caseInsensitive?"i":c),this.prefix=n[0],this.$$paramNames=q}function r(a){M(this,a)}function s(){function a(a){return null!=a?a.toString().replace(/\//g,"%2F"):a}function e(a){return null!=a?a.toString().replace(/%2F/g,"/"):a}function f(a){return this.pattern.test(a)}function i(){return{strict:t,caseInsensitive:p}}function j(a){return H(a)||K(a)&&H(a[a.length-1])}function k(){for(;x.length;){var a=x.shift();if(a.pattern)throw new Error("You cannot override a type's .pattern at runtime.");b.extend(v[a.name],o.invoke(a.def))}}function l(a){M(this,a||{})}O=this;var o,p=!1,t=!0,u=!1,v={},w=!0,x=[],y={string:{encode:a,decode:e,is:f,pattern:/[^/]*/},"int":{encode:a,decode:function(a){return parseInt(a,10)},is:function(a){return G(a)&&this.decode(a.toString())===a},pattern:/\d+/},bool:{encode:function(a){return a?1:0},decode:function(a){return 0!==parseInt(a,10)},is:function(a){return a===!0||a===!1},pattern:/0|1/},date:{encode:function(a){return this.is(a)?[a.getFullYear(),("0"+(a.getMonth()+1)).slice(-2),("0"+a.getDate()).slice(-2)].join("-"):c},decode:function(a){if(this.is(a))return a;var b=this.capture.exec(a);return b?new Date(b[1],b[2]-1,b[3]):c},is:function(a){return a instanceof Date&&!isNaN(a.valueOf())},equals:function(a,b){return this.is(a)&&this.is(b)&&a.toISOString()===b.toISOString()},pattern:/[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2][0-9]|3[0-1])/,capture:/([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/},json:{encode:b.toJson,decode:b.fromJson,is:b.isObject,equals:b.equals,pattern:/[^/]*/},any:{encode:b.identity,decode:b.identity,is:b.identity,equals:b.equals,pattern:/.*/}};s.$$getDefaultValue=function(a){if(!j(a.value))return a.value;if(!o)throw new Error("Injectable functions cannot be called at configuration time");return o.invoke(a.value)},this.caseInsensitive=function(a){return G(a)&&(p=a),p},this.strictMode=function(a){return G(a)&&(t=a),t},this.defaultSquashPolicy=function(a){if(!G(a))return u;if(a!==!0&&a!==!1&&!I(a))throw new Error("Invalid squash policy: "+a+". Valid policies: false, true, arbitrary-string");return u=a,a},this.compile=function(a,b){return new q(a,M(i(),b))},this.isMatcher=function(a){if(!J(a))return!1;var b=!0;return L(q.prototype,function(c,d){H(c)&&(b=b&&G(a[d])&&H(a[d]))}),b},this.type=function(a,b,c){if(!G(b))return v[a];if(v.hasOwnProperty(a))throw new Error("A type named '"+a+"' has already been defined.");return v[a]=new r(M({name:a},b)),c&&(x.push({name:a,def:c}),w||k()),this},L(y,function(a,b){v[b]=new r(M({name:b},a))}),v=d(v,{}),this.$get=["$injector",function(a){return o=a,w=!1,k(),L(y,function(a,b){v[b]||(v[b]=new r(a))}),this}],this.Param=function(a,b,d,e){function f(a){var b=J(a)?g(a):[],c=-1===h(b,"value")&&-1===h(b,"type")&&-1===h(b,"squash")&&-1===h(b,"array");return c&&(a={value:a}),a.$$fn=j(a.value)?a.value:function(){return a.value},a}function i(b,c,d){if(b.type&&c)throw new Error("Param '"+a+"' has two type configurations.");return c?c:b.type?b.type instanceof r?b.type:new r(b.type):"config"===d?v.any:v.string}function k(){var b={array:"search"===e?"auto":!1},c=a.match(/\[\]$/)?{array:!0}:{};return M(b,c,d).array}function l(a,b){var c=a.squash;if(!b||c===!1)return!1;if(!G(c)||null==c)return u;if(c===!0||I(c))return c;throw new Error("Invalid squash policy: '"+c+"'. Valid policies: false, true, or arbitrary string")}function p(a,b,d,e){var f,g,i=[{from:"",to:d||b?c:""},{from:null,to:d||b?c:""}];return f=K(a.replace)?a.replace:[],I(e)&&f.push({from:e,to:c}),g=n(f,function(a){return a.from}),m(i,function(a){return-1===h(g,a.from)}).concat(f)}function q(){if(!o)throw new Error("Injectable functions cannot be called at configuration time");return o.invoke(d.$$fn)}function s(a){function b(a){return function(b){return b.from===a}}function c(a){var c=n(m(w.replace,b(a)),function(a){return a.to});return c.length?c[0]:a}return a=c(a),G(a)?w.type.decode(a):q()}function t(){return"{Param:"+a+" "+b+" squash: '"+z+"' optional: "+y+"}"}var w=this;d=f(d),b=i(d,b,e);var x=k();b=x?b.$asArray(x,"search"===e):b,"string"!==b.name||x||"path"!==e||d.value!==c||(d.value="");var y=d.value!==c,z=l(d,y),A=p(d,x,y,z);M(this,{id:a,type:b,location:e,array:x,squash:z,replace:A,isOptional:y,value:s,dynamic:c,config:d,toString:t})},l.prototype={$$new:function(){return d(this,M(new l,{$$parent:this}))},$$keys:function(){for(var a=[],b=[],c=this,d=g(l.prototype);c;)b.push(c),c=c.$$parent;return b.reverse(),L(b,function(b){L(g(b),function(b){-1===h(a,b)&&-1===h(d,b)&&a.push(b)})}),a},$$values:function(a){var b={},c=this;return L(c.$$keys(),function(d){b[d]=c[d].value(a&&a[d])}),b},$$equals:function(a,b){var c=!0,d=this;return L(d.$$keys(),function(e){var f=a&&a[e],g=b&&b[e];d[e].type.equals(f,g)||(c=!1)}),c},$$validates:function(a){var b,c,d,e=!0,f=this;return L(this.$$keys(),function(g){d=f[g],c=a[g],b=!c&&d.isOptional,e=e&&(b||!!d.type.is(c))}),e},$$parent:c},this.ParamSet=l}function t(a,d){function e(a){var b=/^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(a.source);return null!=b?b[1].replace(/\\(.)/g,"$1"):""}function f(a,b){return a.replace(/\$(\$|\d{1,2})/,function(a,c){return b["$"===c?0:Number(c)]})}function g(a,b,c){if(!c)return!1;var d=a.invoke(b,b,{$match:c});return G(d)?d:!0}function h(d,e,f,g){function h(a,b,c){return"/"===p?a:b?p.slice(0,-1)+a:c?p.slice(1)+a:a}function m(a){function b(a){var b=a(f,d);return b?(I(b)&&d.replace().url(b),!0):!1}if(!a||!a.defaultPrevented){var e=o&&d.url()===o;if(o=c,e)return!0;var g,h=j.length;for(g=0;h>g;g++)if(b(j[g]))return;k&&b(k)}}function n(){return i=i||e.$on("$locationChangeSuccess",m)}var o,p=g.baseHref(),q=d.url();return l||n(),{sync:function(){m()},listen:function(){return n()},update:function(a){return a?void(q=d.url()):void(d.url()!==q&&(d.url(q),d.replace()))},push:function(a,b,e){d.url(a.format(b||{})),o=e&&e.$$avoidResync?d.url():c,e&&e.replace&&d.replace()},href:function(c,e,f){if(!c.validates(e))return null;var g=a.html5Mode();b.isObject(g)&&(g=g.enabled);var i=c.format(e);if(f=f||{},g||null===i||(i="#"+a.hashPrefix()+i),i=h(i,g,f.absolute),!f.absolute||!i)return i;var j=!g&&i?"/":"",k=d.port();return k=80===k||443===k?"":":"+k,[d.protocol(),"://",d.host(),k,j,i].join("")}}}var i,j=[],k=null,l=!1;this.rule=function(a){if(!H(a))throw new Error("'rule' must be a function");return j.push(a),this},this.otherwise=function(a){if(I(a)){var b=a;a=function(){return b}}else if(!H(a))throw new Error("'rule' must be a function");return k=a,this},this.when=function(a,b){var c,h=I(b);if(I(a)&&(a=d.compile(a)),!h&&!H(b)&&!K(b))throw new Error("invalid 'handler' in when()");var i={matcher:function(a,b){return h&&(c=d.compile(b),b=["$match",function(a){return c.format(a)}]),M(function(c,d){return g(c,b,a.exec(d.path(),d.search()))},{prefix:I(a.prefix)?a.prefix:""})},regex:function(a,b){if(a.global||a.sticky)throw new Error("when() RegExp must not be global or sticky");return h&&(c=b,b=["$match",function(a){return f(c,a)}]),M(function(c,d){return g(c,b,a.exec(d.path()))},{prefix:e(a)})}},j={matcher:d.isMatcher(a),regex:a instanceof RegExp};for(var k in j)if(j[k])return this.rule(i[k](a,b));throw new Error("invalid 'what' in when()")},this.deferIntercept=function(a){a===c&&(a=!0),l=a},this.$get=h,h.$inject=["$location","$rootScope","$injector","$browser"]}function u(a,e){function f(a){return 0===a.indexOf(".")||0===a.indexOf("^")}function l(a,b){if(!a)return c;var d=I(a),e=d?a:a.name,g=f(e);if(g){if(!b)throw new Error("No reference point given for path '"+e+"'");b=l(b);for(var h=e.split("."),i=0,j=h.length,k=b;j>i;i++)if(""!==h[i]||0!==i){if("^"!==h[i])break;if(!k.parent)throw new Error("Path '"+e+"' not valid for state '"+b.name+"'");k=k.parent}else k=b;h=h.slice(i).join("."),e=k.name+(k.name&&h?".":"")+h}var m=y[e];return!m||!d&&(d||m!==a&&m.self!==a)?c:m}function m(a,b){z[a]||(z[a]=[]),z[a].push(b)}function o(a){for(var b=z[a]||[];b.length;)p(b.shift())}function p(b){b=d(b,{self:b,resolve:b.resolve||{},toString:function(){return this.name}});var c=b.name;if(!I(c)||c.indexOf("@")>=0)throw new Error("State must have a valid name");if(y.hasOwnProperty(c))throw new Error("State '"+c+"'' is already defined");var e=-1!==c.indexOf(".")?c.substring(0,c.lastIndexOf(".")):I(b.parent)?b.parent:J(b.parent)&&I(b.parent.name)?b.parent.name:"";if(e&&!y[e])return m(e,b.self);for(var f in B)H(B[f])&&(b[f]=B[f](b,B.$delegates[f]));return y[c]=b,!b[A]&&b.url&&a.when(b.url,["$match","$stateParams",function(a,c){x.$current.navigable==b&&j(a,c)||x.transitionTo(b,a,{inherit:!0,location:!1})}]),o(c),b}function q(a){return a.indexOf("*")>-1}function r(a){var b=a.split("."),c=x.$current.name.split(".");if("**"===b[0]&&(c=c.slice(h(c,b[1])),c.unshift("**")),"**"===b[b.length-1]&&(c.splice(h(c,b[b.length-2])+1,Number.MAX_VALUE),c.push("**")),b.length!=c.length)return!1;for(var d=0,e=b.length;e>d;d++)"*"===b[d]&&(c[d]="*");return c.join("")===b.join("")}function s(a,b){return I(a)&&!G(b)?B[a]:H(b)&&I(a)?(B[a]&&!B.$delegates[a]&&(B.$delegates[a]=B[a]),B[a]=b,this):this}function t(a,b){return J(a)?b=a:b.name=a,p(b),this}function u(a,e,f,h,m,o,p){function s(b,c,d,f){var g=a.$broadcast("$stateNotFound",b,c,d);if(g.defaultPrevented)return p.update(),B;if(!g.retry)return null;if(f.$retry)return p.update(),C;var h=x.transition=e.when(g.retry);return h.then(function(){return h!==x.transition?u:(b.options.$retry=!0,x.transitionTo(b.to,b.toParams,b.options))},function(){return B}),p.update(),h}function t(a,c,d,g,i,j){var l=d?c:k(a.params.$$keys(),c),n={$stateParams:l};i.resolve=m.resolve(a.resolve,n,i.resolve,a);var o=[i.resolve.then(function(a){i.globals=a})];return g&&o.push(g),L(a.views,function(c,d){var e=c.resolve&&c.resolve!==a.resolve?c.resolve:{};e.$template=[function(){return f.load(d,{view:c,locals:n,params:l,notify:j.notify})||""}],o.push(m.resolve(e,n,i.resolve,a).then(function(f){if(H(c.controllerProvider)||K(c.controllerProvider)){var g=b.extend({},e,n);f.$$controller=h.invoke(c.controllerProvider,null,g)}else f.$$controller=c.controller;f.$$state=a,f.$$controllerAs=c.controllerAs,i[d]=f}))}),e.all(o).then(function(){return i})}var u=e.reject(new Error("transition superseded")),z=e.reject(new Error("transition prevented")),B=e.reject(new Error("transition aborted")),C=e.reject(new Error("transition failed"));return w.locals={resolve:null,globals:{$stateParams:{}}},x={params:{},current:w.self,$current:w,transition:null},x.reload=function(){return x.transitionTo(x.current,o,{reload:!0,inherit:!1,notify:!0})},x.go=function(a,b,c){return x.transitionTo(a,b,M({inherit:!0,relative:x.$current},c))},x.transitionTo=function(b,c,f){c=c||{},f=M({location:!0,inherit:!1,relative:null,notify:!0,reload:!1,$retry:!1},f||{});var g,j=x.$current,m=x.params,n=j.path,q=l(b,f.relative);if(!G(q)){var r={to:b,toParams:c,options:f},y=s(r,j.self,m,f);if(y)return y;if(b=r.to,c=r.toParams,f=r.options,q=l(b,f.relative),!G(q)){if(!f.relative)throw new Error("No such state '"+b+"'");throw new Error("Could not resolve '"+b+"' from state '"+f.relative+"'")}}if(q[A])throw new Error("Cannot transition to abstract state '"+b+"'");if(f.inherit&&(c=i(o,c||{},x.$current,q)),!q.params.$$validates(c))return C;c=q.params.$$values(c),b=q;var B=b.path,D=0,E=B[D],F=w.locals,H=[];if(!f.reload)for(;E&&E===n[D]&&E.ownParams.$$equals(c,m);)F=H[D]=E.locals,D++,E=B[D];if(v(b,j,F,f))return b.self.reloadOnSearch!==!1&&p.update(),x.transition=null,e.when(x.current);if(c=k(b.params.$$keys(),c||{}),f.notify&&a.$broadcast("$stateChangeStart",b.self,c,j.self,m).defaultPrevented)return p.update(),z;for(var I=e.when(F),J=D;J=D;d--)g=n[d],g.self.onExit&&h.invoke(g.self.onExit,g.self,g.locals.globals),g.locals=null;for(d=D;d=0?e:e+"@"+(f?f.state.name:"")}function A(a,b){var c,d=a.match(/^\s*({[^}]*})\s*$/);if(d&&(a=b+"("+d[1]+")"),c=a.replace(/\n/g," ").match(/^([^(]+?)\s*(\((.*)\))?$/),!c||4!==c.length)throw new Error("Invalid state ref '"+a+"'");return{state:c[1],paramExpr:c[3]||null}}function B(a){var b=a.parent().inheritedData("$uiView");return b&&b.state&&b.state.name?b.state:void 0}function C(a,c){var d=["location","inherit","reload"];return{restrict:"A",require:["?^uiSrefActive","?^uiSrefActiveEq"],link:function(e,f,g,h){var i=A(g.uiSref,a.current.name),j=null,k=B(f)||a.$current,l=null,m="A"===f.prop("tagName"),n="FORM"===f[0].nodeName,o=n?"action":"href",p=!0,q={relative:k,inherit:!0},r=e.$eval(g.uiSrefOpts)||{};b.forEach(d,function(a){a in r&&(q[a]=r[a])});var s=function(c){if(c&&(j=b.copy(c)),p){l=a.href(i.state,j,q);var d=h[1]||h[0];return d&&d.$$setStateInfo(i.state,j),null===l?(p=!1,!1):void g.$set(o,l)}};i.paramExpr&&(e.$watch(i.paramExpr,function(a){a!==j&&s(a)},!0),j=b.copy(e.$eval(i.paramExpr))),s(),n||f.bind("click",function(b){var d=b.which||b.button;if(!(d>1||b.ctrlKey||b.metaKey||b.shiftKey||f.attr("target"))){var e=c(function(){a.go(i.state,j,q)});b.preventDefault();var g=m&&!l?1:0;b.preventDefault=function(){g--<=0&&c.cancel(e)}}})}}}function D(a,b,c){return{restrict:"A",controller:["$scope","$element","$attrs",function(b,d,e){function f(){g()?d.addClass(j):d.removeClass(j)}function g(){return"undefined"!=typeof e.uiSrefActiveEq?h&&a.is(h.name,i):h&&a.includes(h.name,i)}var h,i,j;j=c(e.uiSrefActiveEq||e.uiSrefActive||"",!1)(b),this.$$setStateInfo=function(b,c){h=a.get(b,B(d)),i=c,f()},b.$on("$stateChangeSuccess",f)}]}}function E(a){var b=function(b){return a.is(b)};return b.$stateful=!0,b}function F(a){var b=function(b){return a.includes(b)};return b.$stateful=!0,b}var G=b.isDefined,H=b.isFunction,I=b.isString,J=b.isObject,K=b.isArray,L=b.forEach,M=b.extend,N=b.copy;b.module("ui.router.util",["ng"]),b.module("ui.router.router",["ui.router.util"]),b.module("ui.router.state",["ui.router.router","ui.router.util"]),b.module("ui.router",["ui.router.state"]),b.module("ui.router.compat",["ui.router"]),o.$inject=["$q","$injector"],b.module("ui.router.util").service("$resolve",o),p.$inject=["$http","$templateCache","$injector"],b.module("ui.router.util").service("$templateFactory",p);var O;q.prototype.concat=function(a,b){var c={caseInsensitive:O.caseInsensitive(),strict:O.strictMode(),squash:O.defaultSquashPolicy()};return new q(this.sourcePath+a+this.sourceSearch,M(c,b),this)},q.prototype.toString=function(){return this.source},q.prototype.exec=function(a,b){function c(a){function b(a){return a.split("").reverse().join("")}function c(a){return a.replace(/\\-/,"-")}var d=b(a).split(/-(?!\\)/),e=n(d,b);return n(e,c).reverse()}var d=this.regexp.exec(a);if(!d)return null;b=b||{};var e,f,g,h=this.parameters(),i=h.length,j=this.segments.length-1,k={};if(j!==d.length-1)throw new Error("Unbalanced capture group in route '"+this.source+"'");for(e=0;j>e;e++){g=h[e];var l=this.params[g],m=d[e+1];for(f=0;fe;e++)g=h[e],k[g]=this.params[g].value(b[g]);return k},q.prototype.parameters=function(a){return G(a)?this.params[a]||null:this.$$paramNames},q.prototype.validates=function(a){return this.params.$$validates(a)},q.prototype.format=function(a){function b(a){return encodeURIComponent(a).replace(/-/g,function(a){return"%5C%"+a.charCodeAt(0).toString(16).toUpperCase()})}a=a||{};var c=this.segments,d=this.parameters(),e=this.params;if(!this.validates(a))return null;var f,g=!1,h=c.length-1,i=d.length,j=c[0];for(f=0;i>f;f++){var k=h>f,l=d[f],m=e[l],o=m.value(a[l]),p=m.isOptional&&m.type.equals(m.value(),o),q=p?m.squash:!1,r=m.type.encode(o);if(k){var s=c[f+1];if(q===!1)null!=r&&(j+=K(r)?n(r,b).join("-"):encodeURIComponent(r)),j+=s;else if(q===!0){var t=j.match(/\/$/)?/\/?(.*)/:/(.*)/;j+=s.match(t)[1]}else I(q)&&(j+=q+s)}else{if(null==r||p&&q!==!1)continue;K(r)||(r=[r]),r=n(r,encodeURIComponent).join("&"+l+"="),j+=(g?"&":"?")+(l+"="+r),g=!0}}return j},r.prototype.is=function(){return!0},r.prototype.encode=function(a){return a},r.prototype.decode=function(a){return a},r.prototype.equals=function(a,b){return a==b},r.prototype.$subPattern=function(){var a=this.pattern.toString();return a.substr(1,a.length-2)},r.prototype.pattern=/.*/,r.prototype.toString=function(){return"{Type:"+this.name+"}"},r.prototype.$asArray=function(a,b){function d(a,b){function d(a,b){return function(){return a[b].apply(a,arguments)}}function e(a){return K(a)?a:G(a)?[a]:[]}function f(a){switch(a.length){case 0:return c;case 1:return"auto"===b?a[0]:a;default:return a}}function g(a){return!a}function h(a,b){return function(c){c=e(c);var d=n(c,a);return b===!0?0===m(d,g).length:f(d)}}function i(a){return function(b,c){var d=e(b),f=e(c);if(d.length!==f.length)return!1;for(var g=0;g 0) { + className += (i > 0) ? ' ' : ''; + className += isPrefix ? fix + klass + : klass + fix; + } + }); + return className; +} + +function removeFromArray(arr, val) { + var index = arr.indexOf(val); + if (val >= 0) { + arr.splice(index, 1); + } +} + +function stripCommentsFromElement(element) { + if (element instanceof jqLite) { + switch (element.length) { + case 0: + return []; + break; + + case 1: + // there is no point of stripping anything if the element + // is the only element within the jqLite wrapper. + // (it's important that we retain the element instance.) + if (element[0].nodeType === ELEMENT_NODE) { + return element; + } + break; + + default: + return jqLite(extractElementNode(element)); + break; + } + } + + if (element.nodeType === ELEMENT_NODE) { + return jqLite(element); + } +} + +function extractElementNode(element) { + if (!element[0]) return element; + for (var i = 0; i < element.length; i++) { + var elm = element[i]; + if (elm.nodeType == ELEMENT_NODE) { + return elm; + } + } +} + +function $$addClass($$jqLite, element, className) { + forEach(element, function(elm) { + $$jqLite.addClass(elm, className); + }); +} + +function $$removeClass($$jqLite, element, className) { + forEach(element, function(elm) { + $$jqLite.removeClass(elm, className); + }); +} + +function applyAnimationClassesFactory($$jqLite) { + return function(element, options) { + if (options.addClass) { + $$addClass($$jqLite, element, options.addClass); + options.addClass = null; + } + if (options.removeClass) { + $$removeClass($$jqLite, element, options.removeClass); + options.removeClass = null; + } + } +} + +function prepareAnimationOptions(options) { + options = options || {}; + if (!options.$$prepared) { + var domOperation = options.domOperation || noop; + options.domOperation = function() { + options.$$domOperationFired = true; + domOperation(); + domOperation = noop; + }; + options.$$prepared = true; + } + return options; +} + +function applyAnimationStyles(element, options) { + applyAnimationFromStyles(element, options); + applyAnimationToStyles(element, options); +} + +function applyAnimationFromStyles(element, options) { + if (options.from) { + element.css(options.from); + options.from = null; + } +} + +function applyAnimationToStyles(element, options) { + if (options.to) { + element.css(options.to); + options.to = null; + } +} + +function mergeAnimationDetails(element, oldAnimation, newAnimation) { + var target = oldAnimation.options || {}; + var newOptions = newAnimation.options || {}; + + var toAdd = (target.addClass || '') + ' ' + (newOptions.addClass || ''); + var toRemove = (target.removeClass || '') + ' ' + (newOptions.removeClass || ''); + var classes = resolveElementClasses(element.attr('class'), toAdd, toRemove); + + if (newOptions.preparationClasses) { + target.preparationClasses = concatWithSpace(newOptions.preparationClasses, target.preparationClasses); + delete newOptions.preparationClasses; + } + + // noop is basically when there is no callback; otherwise something has been set + var realDomOperation = target.domOperation !== noop ? target.domOperation : null; + + extend(target, newOptions); + + // TODO(matsko or sreeramu): proper fix is to maintain all animation callback in array and call at last,but now only leave has the callback so no issue with this. + if (realDomOperation) { + target.domOperation = realDomOperation; + } + + if (classes.addClass) { + target.addClass = classes.addClass; + } else { + target.addClass = null; + } + + if (classes.removeClass) { + target.removeClass = classes.removeClass; + } else { + target.removeClass = null; + } + + oldAnimation.addClass = target.addClass; + oldAnimation.removeClass = target.removeClass; + + return target; +} + +function resolveElementClasses(existing, toAdd, toRemove) { + var ADD_CLASS = 1; + var REMOVE_CLASS = -1; + + var flags = {}; + existing = splitClassesToLookup(existing); + + toAdd = splitClassesToLookup(toAdd); + forEach(toAdd, function(value, key) { + flags[key] = ADD_CLASS; + }); + + toRemove = splitClassesToLookup(toRemove); + forEach(toRemove, function(value, key) { + flags[key] = flags[key] === ADD_CLASS ? null : REMOVE_CLASS; + }); + + var classes = { + addClass: '', + removeClass: '' + }; + + forEach(flags, function(val, klass) { + var prop, allow; + if (val === ADD_CLASS) { + prop = 'addClass'; + allow = !existing[klass]; + } else if (val === REMOVE_CLASS) { + prop = 'removeClass'; + allow = existing[klass]; + } + if (allow) { + if (classes[prop].length) { + classes[prop] += ' '; + } + classes[prop] += klass; + } + }); + + function splitClassesToLookup(classes) { + if (isString(classes)) { + classes = classes.split(' '); + } + + var obj = {}; + forEach(classes, function(klass) { + // sometimes the split leaves empty string values + // incase extra spaces were applied to the options + if (klass.length) { + obj[klass] = true; + } + }); + return obj; + } + + return classes; +} + +function getDomNode(element) { + return (element instanceof angular.element) ? element[0] : element; +} + +function applyGeneratedPreparationClasses(element, event, options) { + var classes = ''; + if (event) { + classes = pendClasses(event, EVENT_CLASS_PREFIX, true); + } + if (options.addClass) { + classes = concatWithSpace(classes, pendClasses(options.addClass, ADD_CLASS_SUFFIX)); + } + if (options.removeClass) { + classes = concatWithSpace(classes, pendClasses(options.removeClass, REMOVE_CLASS_SUFFIX)); + } + if (classes.length) { + options.preparationClasses = classes; + element.addClass(classes); + } +} + +function clearGeneratedClasses(element, options) { + if (options.preparationClasses) { + element.removeClass(options.preparationClasses); + options.preparationClasses = null; + } + if (options.activeClasses) { + element.removeClass(options.activeClasses); + options.activeClasses = null; + } +} + +function blockTransitions(node, duration) { + // we use a negative delay value since it performs blocking + // yet it doesn't kill any existing transitions running on the + // same element which makes this safe for class-based animations + var value = duration ? '-' + duration + 's' : ''; + applyInlineStyle(node, [TRANSITION_DELAY_PROP, value]); + return [TRANSITION_DELAY_PROP, value]; +} + +function blockKeyframeAnimations(node, applyBlock) { + var value = applyBlock ? 'paused' : ''; + var key = ANIMATION_PROP + ANIMATION_PLAYSTATE_KEY; + applyInlineStyle(node, [key, value]); + return [key, value]; +} + +function applyInlineStyle(node, styleTuple) { + var prop = styleTuple[0]; + var value = styleTuple[1]; + node.style[prop] = value; +} + +function concatWithSpace(a,b) { + if (!a) return b; + if (!b) return a; + return a + ' ' + b; +} + +var $$rAFSchedulerFactory = ['$$rAF', function($$rAF) { + var queue, cancelFn; + + function scheduler(tasks) { + // we make a copy since RAFScheduler mutates the state + // of the passed in array variable and this would be difficult + // to track down on the outside code + queue = queue.concat(tasks); + nextTick(); + } + + queue = scheduler.queue = []; + + /* waitUntilQuiet does two things: + * 1. It will run the FINAL `fn` value only when an uncanceled RAF has passed through + * 2. It will delay the next wave of tasks from running until the quiet `fn` has run. + * + * The motivation here is that animation code can request more time from the scheduler + * before the next wave runs. This allows for certain DOM properties such as classes to + * be resolved in time for the next animation to run. + */ + scheduler.waitUntilQuiet = function(fn) { + if (cancelFn) cancelFn(); + + cancelFn = $$rAF(function() { + cancelFn = null; + fn(); + nextTick(); + }); + }; + + return scheduler; + + function nextTick() { + if (!queue.length) return; + + var items = queue.shift(); + for (var i = 0; i < items.length; i++) { + items[i](); + } + + if (!cancelFn) { + $$rAF(function() { + if (!cancelFn) nextTick(); + }); + } + } +}]; + +/** + * @ngdoc directive + * @name ngAnimateChildren + * @restrict AE + * @element ANY + * + * @description + * + * ngAnimateChildren allows you to specify that children of this element should animate even if any + * of the children's parents are currently animating. By default, when an element has an active `enter`, `leave`, or `move` + * (structural) animation, child elements that also have an active structural animation are not animated. + * + * Note that even if `ngAnimteChildren` is set, no child animations will run when the parent element is removed from the DOM (`leave` animation). + * + * + * @param {string} ngAnimateChildren If the value is empty, `true` or `on`, + * then child animations are allowed. If the value is `false`, child animations are not allowed. + * + * @example + * + +
    + + +
    +
    +
    + List of items: +
    Item {{item}}
    +
    +
    +
    +
    + + + .container.ng-enter, + .container.ng-leave { + transition: all ease 1.5s; + } + + .container.ng-enter, + .container.ng-leave-active { + opacity: 0; + } + + .container.ng-leave, + .container.ng-enter-active { + opacity: 1; + } + + .item { + background: firebrick; + color: #FFF; + margin-bottom: 10px; + } + + .item.ng-enter, + .item.ng-leave { + transition: transform 1.5s ease; + } + + .item.ng-enter { + transform: translateX(50px); + } + + .item.ng-enter-active { + transform: translateX(0); + } + + + angular.module('ngAnimateChildren', ['ngAnimate']) + .controller('mainController', function() { + this.animateChildren = false; + this.enterElement = false; + }); + +
    + */ +var $$AnimateChildrenDirective = ['$interpolate', function($interpolate) { + return { + link: function(scope, element, attrs) { + var val = attrs.ngAnimateChildren; + if (angular.isString(val) && val.length === 0) { //empty attribute + element.data(NG_ANIMATE_CHILDREN_DATA, true); + } else { + // Interpolate and set the value, so that it is available to + // animations that run right after compilation + setData($interpolate(val)(scope)); + attrs.$observe('ngAnimateChildren', setData); + } + + function setData(value) { + value = value === 'on' || value === 'true'; + element.data(NG_ANIMATE_CHILDREN_DATA, value); + } + } + }; +}]; + +var ANIMATE_TIMER_KEY = '$$animateCss'; + +/** + * @ngdoc service + * @name $animateCss + * @kind object + * + * @description + * The `$animateCss` service is a useful utility to trigger customized CSS-based transitions/keyframes + * from a JavaScript-based animation or directly from a directive. The purpose of `$animateCss` is NOT + * to side-step how `$animate` and ngAnimate work, but the goal is to allow pre-existing animations or + * directives to create more complex animations that can be purely driven using CSS code. + * + * Note that only browsers that support CSS transitions and/or keyframe animations are capable of + * rendering animations triggered via `$animateCss` (bad news for IE9 and lower). + * + * ## Usage + * Once again, `$animateCss` is designed to be used inside of a registered JavaScript animation that + * is powered by ngAnimate. It is possible to use `$animateCss` directly inside of a directive, however, + * any automatic control over cancelling animations and/or preventing animations from being run on + * child elements will not be handled by Angular. For this to work as expected, please use `$animate` to + * trigger the animation and then setup a JavaScript animation that injects `$animateCss` to trigger + * the CSS animation. + * + * The example below shows how we can create a folding animation on an element using `ng-if`: + * + * ```html + * + *
    + * This element will go BOOM + *
    + * + * ``` + * + * Now we create the **JavaScript animation** that will trigger the CSS transition: + * + * ```js + * ngModule.animation('.fold-animation', ['$animateCss', function($animateCss) { + * return { + * enter: function(element, doneFn) { + * var height = element[0].offsetHeight; + * return $animateCss(element, { + * from: { height:'0px' }, + * to: { height:height + 'px' }, + * duration: 1 // one second + * }); + * } + * } + * }]); + * ``` + * + * ## More Advanced Uses + * + * `$animateCss` is the underlying code that ngAnimate uses to power **CSS-based animations** behind the scenes. Therefore CSS hooks + * like `.ng-EVENT`, `.ng-EVENT-active`, `.ng-EVENT-stagger` are all features that can be triggered using `$animateCss` via JavaScript code. + * + * This also means that just about any combination of adding classes, removing classes, setting styles, dynamically setting a keyframe animation, + * applying a hardcoded duration or delay value, changing the animation easing or applying a stagger animation are all options that work with + * `$animateCss`. The service itself is smart enough to figure out the combination of options and examine the element styling properties in order + * to provide a working animation that will run in CSS. + * + * The example below showcases a more advanced version of the `.fold-animation` from the example above: + * + * ```js + * ngModule.animation('.fold-animation', ['$animateCss', function($animateCss) { + * return { + * enter: function(element, doneFn) { + * var height = element[0].offsetHeight; + * return $animateCss(element, { + * addClass: 'red large-text pulse-twice', + * easing: 'ease-out', + * from: { height:'0px' }, + * to: { height:height + 'px' }, + * duration: 1 // one second + * }); + * } + * } + * }]); + * ``` + * + * Since we're adding/removing CSS classes then the CSS transition will also pick those up: + * + * ```css + * /* since a hardcoded duration value of 1 was provided in the JavaScript animation code, + * the CSS classes below will be transitioned despite them being defined as regular CSS classes */ + * .red { background:red; } + * .large-text { font-size:20px; } + * + * /* we can also use a keyframe animation and $animateCss will make it work alongside the transition */ + * .pulse-twice { + * animation: 0.5s pulse linear 2; + * -webkit-animation: 0.5s pulse linear 2; + * } + * + * @keyframes pulse { + * from { transform: scale(0.5); } + * to { transform: scale(1.5); } + * } + * + * @-webkit-keyframes pulse { + * from { -webkit-transform: scale(0.5); } + * to { -webkit-transform: scale(1.5); } + * } + * ``` + * + * Given this complex combination of CSS classes, styles and options, `$animateCss` will figure everything out and make the animation happen. + * + * ## How the Options are handled + * + * `$animateCss` is very versatile and intelligent when it comes to figuring out what configurations to apply to the element to ensure the animation + * works with the options provided. Say for example we were adding a class that contained a keyframe value and we wanted to also animate some inline + * styles using the `from` and `to` properties. + * + * ```js + * var animator = $animateCss(element, { + * from: { background:'red' }, + * to: { background:'blue' } + * }); + * animator.start(); + * ``` + * + * ```css + * .rotating-animation { + * animation:0.5s rotate linear; + * -webkit-animation:0.5s rotate linear; + * } + * + * @keyframes rotate { + * from { transform: rotate(0deg); } + * to { transform: rotate(360deg); } + * } + * + * @-webkit-keyframes rotate { + * from { -webkit-transform: rotate(0deg); } + * to { -webkit-transform: rotate(360deg); } + * } + * ``` + * + * The missing pieces here are that we do not have a transition set (within the CSS code nor within the `$animateCss` options) and the duration of the animation is + * going to be detected from what the keyframe styles on the CSS class are. In this event, `$animateCss` will automatically create an inline transition + * style matching the duration detected from the keyframe style (which is present in the CSS class that is being added) and then prepare both the transition + * and keyframe animations to run in parallel on the element. Then when the animation is underway the provided `from` and `to` CSS styles will be applied + * and spread across the transition and keyframe animation. + * + * ## What is returned + * + * `$animateCss` works in two stages: a preparation phase and an animation phase. Therefore when `$animateCss` is first called it will NOT actually + * start the animation. All that is going on here is that the element is being prepared for the animation (which means that the generated CSS classes are + * added and removed on the element). Once `$animateCss` is called it will return an object with the following properties: + * + * ```js + * var animator = $animateCss(element, { ... }); + * ``` + * + * Now what do the contents of our `animator` variable look like: + * + * ```js + * { + * // starts the animation + * start: Function, + * + * // ends (aborts) the animation + * end: Function + * } + * ``` + * + * To actually start the animation we need to run `animation.start()` which will then return a promise that we can hook into to detect when the animation ends. + * If we choose not to run the animation then we MUST run `animation.end()` to perform a cleanup on the element (since some CSS classes and styles may have been + * applied to the element during the preparation phase). Note that all other properties such as duration, delay, transitions and keyframes are just properties + * and that changing them will not reconfigure the parameters of the animation. + * + * ### runner.done() vs runner.then() + * It is documented that `animation.start()` will return a promise object and this is true, however, there is also an additional method available on the + * runner called `.done(callbackFn)`. The done method works the same as `.finally(callbackFn)`, however, it does **not trigger a digest to occur**. + * Therefore, for performance reasons, it's always best to use `runner.done(callback)` instead of `runner.then()`, `runner.catch()` or `runner.finally()` + * unless you really need a digest to kick off afterwards. + * + * Keep in mind that, to make this easier, ngAnimate has tweaked the JS animations API to recognize when a runner instance is returned from $animateCss + * (so there is no need to call `runner.done(doneFn)` inside of your JavaScript animation code). + * Check the {@link ngAnimate.$animateCss#usage animation code above} to see how this works. + * + * @param {DOMElement} element the element that will be animated + * @param {object} options the animation-related options that will be applied during the animation + * + * * `event` - The DOM event (e.g. enter, leave, move). When used, a generated CSS class of `ng-EVENT` and `ng-EVENT-active` will be applied + * to the element during the animation. Multiple events can be provided when spaces are used as a separator. (Note that this will not perform any DOM operation.) + * * `structural` - Indicates that the `ng-` prefix will be added to the event class. Setting to `false` or omitting will turn `ng-EVENT` and + * `ng-EVENT-active` in `EVENT` and `EVENT-active`. Unused if `event` is omitted. + * * `easing` - The CSS easing value that will be applied to the transition or keyframe animation (or both). + * * `transitionStyle` - The raw CSS transition style that will be used (e.g. `1s linear all`). + * * `keyframeStyle` - The raw CSS keyframe animation style that will be used (e.g. `1s my_animation linear`). + * * `from` - The starting CSS styles (a key/value object) that will be applied at the start of the animation. + * * `to` - The ending CSS styles (a key/value object) that will be applied across the animation via a CSS transition. + * * `addClass` - A space separated list of CSS classes that will be added to the element and spread across the animation. + * * `removeClass` - A space separated list of CSS classes that will be removed from the element and spread across the animation. + * * `duration` - A number value representing the total duration of the transition and/or keyframe (note that a value of 1 is 1000ms). If a value of `0` + * is provided then the animation will be skipped entirely. + * * `delay` - A number value representing the total delay of the transition and/or keyframe (note that a value of 1 is 1000ms). If a value of `true` is + * used then whatever delay value is detected from the CSS classes will be mirrored on the elements styles (e.g. by setting delay true then the style value + * of the element will be `transition-delay: DETECTED_VALUE`). Using `true` is useful when you want the CSS classes and inline styles to all share the same + * CSS delay value. + * * `stagger` - A numeric time value representing the delay between successively animated elements + * ({@link ngAnimate#css-staggering-animations Click here to learn how CSS-based staggering works in ngAnimate.}) + * * `staggerIndex` - The numeric index representing the stagger item (e.g. a value of 5 is equal to the sixth item in the stagger; therefore when a + * `stagger` option value of `0.1` is used then there will be a stagger delay of `600ms`) + * * `applyClassesEarly` - Whether or not the classes being added or removed will be used when detecting the animation. This is set by `$animate` when enter/leave/move animations are fired to ensure that the CSS classes are resolved in time. (Note that this will prevent any transitions from occurring on the classes being added and removed.) + * * `cleanupStyles` - Whether or not the provided `from` and `to` styles will be removed once + * the animation is closed. This is useful for when the styles are used purely for the sake of + * the animation and do not have a lasting visual effect on the element (e.g. a collapse and open animation). + * By default this value is set to `false`. + * + * @return {object} an object with start and end methods and details about the animation. + * + * * `start` - The method to start the animation. This will return a `Promise` when called. + * * `end` - This method will cancel the animation and remove all applied CSS classes and styles. + */ +var ONE_SECOND = 1000; +var BASE_TEN = 10; + +var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3; +var CLOSING_TIME_BUFFER = 1.5; + +var DETECT_CSS_PROPERTIES = { + transitionDuration: TRANSITION_DURATION_PROP, + transitionDelay: TRANSITION_DELAY_PROP, + transitionProperty: TRANSITION_PROP + PROPERTY_KEY, + animationDuration: ANIMATION_DURATION_PROP, + animationDelay: ANIMATION_DELAY_PROP, + animationIterationCount: ANIMATION_PROP + ANIMATION_ITERATION_COUNT_KEY +}; + +var DETECT_STAGGER_CSS_PROPERTIES = { + transitionDuration: TRANSITION_DURATION_PROP, + transitionDelay: TRANSITION_DELAY_PROP, + animationDuration: ANIMATION_DURATION_PROP, + animationDelay: ANIMATION_DELAY_PROP +}; + +function getCssKeyframeDurationStyle(duration) { + return [ANIMATION_DURATION_PROP, duration + 's']; +} + +function getCssDelayStyle(delay, isKeyframeAnimation) { + var prop = isKeyframeAnimation ? ANIMATION_DELAY_PROP : TRANSITION_DELAY_PROP; + return [prop, delay + 's']; +} + +function computeCssStyles($window, element, properties) { + var styles = Object.create(null); + var detectedStyles = $window.getComputedStyle(element) || {}; + forEach(properties, function(formalStyleName, actualStyleName) { + var val = detectedStyles[formalStyleName]; + if (val) { + var c = val.charAt(0); + + // only numerical-based values have a negative sign or digit as the first value + if (c === '-' || c === '+' || c >= 0) { + val = parseMaxTime(val); + } + + // by setting this to null in the event that the delay is not set or is set directly as 0 + // then we can still allow for negative values to be used later on and not mistake this + // value for being greater than any other negative value. + if (val === 0) { + val = null; + } + styles[actualStyleName] = val; + } + }); + + return styles; +} + +function parseMaxTime(str) { + var maxValue = 0; + var values = str.split(/\s*,\s*/); + forEach(values, function(value) { + // it's always safe to consider only second values and omit `ms` values since + // getComputedStyle will always handle the conversion for us + if (value.charAt(value.length - 1) == 's') { + value = value.substring(0, value.length - 1); + } + value = parseFloat(value) || 0; + maxValue = maxValue ? Math.max(value, maxValue) : value; + }); + return maxValue; +} + +function truthyTimingValue(val) { + return val === 0 || val != null; +} + +function getCssTransitionDurationStyle(duration, applyOnlyDuration) { + var style = TRANSITION_PROP; + var value = duration + 's'; + if (applyOnlyDuration) { + style += DURATION_KEY; + } else { + value += ' linear all'; + } + return [style, value]; +} + +function createLocalCacheLookup() { + var cache = Object.create(null); + return { + flush: function() { + cache = Object.create(null); + }, + + count: function(key) { + var entry = cache[key]; + return entry ? entry.total : 0; + }, + + get: function(key) { + var entry = cache[key]; + return entry && entry.value; + }, + + put: function(key, value) { + if (!cache[key]) { + cache[key] = { total: 1, value: value }; + } else { + cache[key].total++; + } + } + }; +} + +// we do not reassign an already present style value since +// if we detect the style property value again we may be +// detecting styles that were added via the `from` styles. +// We make use of `isDefined` here since an empty string +// or null value (which is what getPropertyValue will return +// for a non-existing style) will still be marked as a valid +// value for the style (a falsy value implies that the style +// is to be removed at the end of the animation). If we had a simple +// "OR" statement then it would not be enough to catch that. +function registerRestorableStyles(backup, node, properties) { + forEach(properties, function(prop) { + backup[prop] = isDefined(backup[prop]) + ? backup[prop] + : node.style.getPropertyValue(prop); + }); +} + +var $AnimateCssProvider = ['$animateProvider', function($animateProvider) { + var gcsLookup = createLocalCacheLookup(); + var gcsStaggerLookup = createLocalCacheLookup(); + + this.$get = ['$window', '$$jqLite', '$$AnimateRunner', '$timeout', + '$$forceReflow', '$sniffer', '$$rAFScheduler', '$$animateQueue', + function($window, $$jqLite, $$AnimateRunner, $timeout, + $$forceReflow, $sniffer, $$rAFScheduler, $$animateQueue) { + + var applyAnimationClasses = applyAnimationClassesFactory($$jqLite); + + var parentCounter = 0; + function gcsHashFn(node, extraClasses) { + var KEY = "$$ngAnimateParentKey"; + var parentNode = node.parentNode; + var parentID = parentNode[KEY] || (parentNode[KEY] = ++parentCounter); + return parentID + '-' + node.getAttribute('class') + '-' + extraClasses; + } + + function computeCachedCssStyles(node, className, cacheKey, properties) { + var timings = gcsLookup.get(cacheKey); + + if (!timings) { + timings = computeCssStyles($window, node, properties); + if (timings.animationIterationCount === 'infinite') { + timings.animationIterationCount = 1; + } + } + + // we keep putting this in multiple times even though the value and the cacheKey are the same + // because we're keeping an internal tally of how many duplicate animations are detected. + gcsLookup.put(cacheKey, timings); + return timings; + } + + function computeCachedCssStaggerStyles(node, className, cacheKey, properties) { + var stagger; + + // if we have one or more existing matches of matching elements + // containing the same parent + CSS styles (which is how cacheKey works) + // then staggering is possible + if (gcsLookup.count(cacheKey) > 0) { + stagger = gcsStaggerLookup.get(cacheKey); + + if (!stagger) { + var staggerClassName = pendClasses(className, '-stagger'); + + $$jqLite.addClass(node, staggerClassName); + + stagger = computeCssStyles($window, node, properties); + + // force the conversion of a null value to zero incase not set + stagger.animationDuration = Math.max(stagger.animationDuration, 0); + stagger.transitionDuration = Math.max(stagger.transitionDuration, 0); + + $$jqLite.removeClass(node, staggerClassName); + + gcsStaggerLookup.put(cacheKey, stagger); + } + } + + return stagger || {}; + } + + var cancelLastRAFRequest; + var rafWaitQueue = []; + function waitUntilQuiet(callback) { + rafWaitQueue.push(callback); + $$rAFScheduler.waitUntilQuiet(function() { + gcsLookup.flush(); + gcsStaggerLookup.flush(); + + // DO NOT REMOVE THIS LINE OR REFACTOR OUT THE `pageWidth` variable. + // PLEASE EXAMINE THE `$$forceReflow` service to understand why. + var pageWidth = $$forceReflow(); + + // we use a for loop to ensure that if the queue is changed + // during this looping then it will consider new requests + for (var i = 0; i < rafWaitQueue.length; i++) { + rafWaitQueue[i](pageWidth); + } + rafWaitQueue.length = 0; + }); + } + + function computeTimings(node, className, cacheKey) { + var timings = computeCachedCssStyles(node, className, cacheKey, DETECT_CSS_PROPERTIES); + var aD = timings.animationDelay; + var tD = timings.transitionDelay; + timings.maxDelay = aD && tD + ? Math.max(aD, tD) + : (aD || tD); + timings.maxDuration = Math.max( + timings.animationDuration * timings.animationIterationCount, + timings.transitionDuration); + + return timings; + } + + return function init(element, initialOptions) { + // all of the animation functions should create + // a copy of the options data, however, if a + // parent service has already created a copy then + // we should stick to using that + var options = initialOptions || {}; + if (!options.$$prepared) { + options = prepareAnimationOptions(copy(options)); + } + + var restoreStyles = {}; + var node = getDomNode(element); + if (!node + || !node.parentNode + || !$$animateQueue.enabled()) { + return closeAndReturnNoopAnimator(); + } + + var temporaryStyles = []; + var classes = element.attr('class'); + var styles = packageStyles(options); + var animationClosed; + var animationPaused; + var animationCompleted; + var runner; + var runnerHost; + var maxDelay; + var maxDelayTime; + var maxDuration; + var maxDurationTime; + var startTime; + var events = []; + + if (options.duration === 0 || (!$sniffer.animations && !$sniffer.transitions)) { + return closeAndReturnNoopAnimator(); + } + + var method = options.event && isArray(options.event) + ? options.event.join(' ') + : options.event; + + var isStructural = method && options.structural; + var structuralClassName = ''; + var addRemoveClassName = ''; + + if (isStructural) { + structuralClassName = pendClasses(method, EVENT_CLASS_PREFIX, true); + } else if (method) { + structuralClassName = method; + } + + if (options.addClass) { + addRemoveClassName += pendClasses(options.addClass, ADD_CLASS_SUFFIX); + } + + if (options.removeClass) { + if (addRemoveClassName.length) { + addRemoveClassName += ' '; + } + addRemoveClassName += pendClasses(options.removeClass, REMOVE_CLASS_SUFFIX); + } + + // there may be a situation where a structural animation is combined together + // with CSS classes that need to resolve before the animation is computed. + // However this means that there is no explicit CSS code to block the animation + // from happening (by setting 0s none in the class name). If this is the case + // we need to apply the classes before the first rAF so we know to continue if + // there actually is a detected transition or keyframe animation + if (options.applyClassesEarly && addRemoveClassName.length) { + applyAnimationClasses(element, options); + } + + var preparationClasses = [structuralClassName, addRemoveClassName].join(' ').trim(); + var fullClassName = classes + ' ' + preparationClasses; + var activeClasses = pendClasses(preparationClasses, ACTIVE_CLASS_SUFFIX); + var hasToStyles = styles.to && Object.keys(styles.to).length > 0; + var containsKeyframeAnimation = (options.keyframeStyle || '').length > 0; + + // there is no way we can trigger an animation if no styles and + // no classes are being applied which would then trigger a transition, + // unless there a is raw keyframe value that is applied to the element. + if (!containsKeyframeAnimation + && !hasToStyles + && !preparationClasses) { + return closeAndReturnNoopAnimator(); + } + + var cacheKey, stagger; + if (options.stagger > 0) { + var staggerVal = parseFloat(options.stagger); + stagger = { + transitionDelay: staggerVal, + animationDelay: staggerVal, + transitionDuration: 0, + animationDuration: 0 + }; + } else { + cacheKey = gcsHashFn(node, fullClassName); + stagger = computeCachedCssStaggerStyles(node, preparationClasses, cacheKey, DETECT_STAGGER_CSS_PROPERTIES); + } + + if (!options.$$skipPreparationClasses) { + $$jqLite.addClass(element, preparationClasses); + } + + var applyOnlyDuration; + + if (options.transitionStyle) { + var transitionStyle = [TRANSITION_PROP, options.transitionStyle]; + applyInlineStyle(node, transitionStyle); + temporaryStyles.push(transitionStyle); + } + + if (options.duration >= 0) { + applyOnlyDuration = node.style[TRANSITION_PROP].length > 0; + var durationStyle = getCssTransitionDurationStyle(options.duration, applyOnlyDuration); + + // we set the duration so that it will be picked up by getComputedStyle later + applyInlineStyle(node, durationStyle); + temporaryStyles.push(durationStyle); + } + + if (options.keyframeStyle) { + var keyframeStyle = [ANIMATION_PROP, options.keyframeStyle]; + applyInlineStyle(node, keyframeStyle); + temporaryStyles.push(keyframeStyle); + } + + var itemIndex = stagger + ? options.staggerIndex >= 0 + ? options.staggerIndex + : gcsLookup.count(cacheKey) + : 0; + + var isFirst = itemIndex === 0; + + // this is a pre-emptive way of forcing the setup classes to be added and applied INSTANTLY + // without causing any combination of transitions to kick in. By adding a negative delay value + // it forces the setup class' transition to end immediately. We later then remove the negative + // transition delay to allow for the transition to naturally do it's thing. The beauty here is + // that if there is no transition defined then nothing will happen and this will also allow + // other transitions to be stacked on top of each other without any chopping them out. + if (isFirst && !options.skipBlocking) { + blockTransitions(node, SAFE_FAST_FORWARD_DURATION_VALUE); + } + + var timings = computeTimings(node, fullClassName, cacheKey); + var relativeDelay = timings.maxDelay; + maxDelay = Math.max(relativeDelay, 0); + maxDuration = timings.maxDuration; + + var flags = {}; + flags.hasTransitions = timings.transitionDuration > 0; + flags.hasAnimations = timings.animationDuration > 0; + flags.hasTransitionAll = flags.hasTransitions && timings.transitionProperty == 'all'; + flags.applyTransitionDuration = hasToStyles && ( + (flags.hasTransitions && !flags.hasTransitionAll) + || (flags.hasAnimations && !flags.hasTransitions)); + flags.applyAnimationDuration = options.duration && flags.hasAnimations; + flags.applyTransitionDelay = truthyTimingValue(options.delay) && (flags.applyTransitionDuration || flags.hasTransitions); + flags.applyAnimationDelay = truthyTimingValue(options.delay) && flags.hasAnimations; + flags.recalculateTimingStyles = addRemoveClassName.length > 0; + + if (flags.applyTransitionDuration || flags.applyAnimationDuration) { + maxDuration = options.duration ? parseFloat(options.duration) : maxDuration; + + if (flags.applyTransitionDuration) { + flags.hasTransitions = true; + timings.transitionDuration = maxDuration; + applyOnlyDuration = node.style[TRANSITION_PROP + PROPERTY_KEY].length > 0; + temporaryStyles.push(getCssTransitionDurationStyle(maxDuration, applyOnlyDuration)); + } + + if (flags.applyAnimationDuration) { + flags.hasAnimations = true; + timings.animationDuration = maxDuration; + temporaryStyles.push(getCssKeyframeDurationStyle(maxDuration)); + } + } + + if (maxDuration === 0 && !flags.recalculateTimingStyles) { + return closeAndReturnNoopAnimator(); + } + + if (options.delay != null) { + var delayStyle; + if (typeof options.delay !== "boolean") { + delayStyle = parseFloat(options.delay); + // number in options.delay means we have to recalculate the delay for the closing timeout + maxDelay = Math.max(delayStyle, 0); + } + + if (flags.applyTransitionDelay) { + temporaryStyles.push(getCssDelayStyle(delayStyle)); + } + + if (flags.applyAnimationDelay) { + temporaryStyles.push(getCssDelayStyle(delayStyle, true)); + } + } + + // we need to recalculate the delay value since we used a pre-emptive negative + // delay value and the delay value is required for the final event checking. This + // property will ensure that this will happen after the RAF phase has passed. + if (options.duration == null && timings.transitionDuration > 0) { + flags.recalculateTimingStyles = flags.recalculateTimingStyles || isFirst; + } + + maxDelayTime = maxDelay * ONE_SECOND; + maxDurationTime = maxDuration * ONE_SECOND; + if (!options.skipBlocking) { + flags.blockTransition = timings.transitionDuration > 0; + flags.blockKeyframeAnimation = timings.animationDuration > 0 && + stagger.animationDelay > 0 && + stagger.animationDuration === 0; + } + + if (options.from) { + if (options.cleanupStyles) { + registerRestorableStyles(restoreStyles, node, Object.keys(options.from)); + } + applyAnimationFromStyles(element, options); + } + + if (flags.blockTransition || flags.blockKeyframeAnimation) { + applyBlocking(maxDuration); + } else if (!options.skipBlocking) { + blockTransitions(node, false); + } + + // TODO(matsko): for 1.5 change this code to have an animator object for better debugging + return { + $$willAnimate: true, + end: endFn, + start: function() { + if (animationClosed) return; + + runnerHost = { + end: endFn, + cancel: cancelFn, + resume: null, //this will be set during the start() phase + pause: null + }; + + runner = new $$AnimateRunner(runnerHost); + + waitUntilQuiet(start); + + // we don't have access to pause/resume the animation + // since it hasn't run yet. AnimateRunner will therefore + // set noop functions for resume and pause and they will + // later be overridden once the animation is triggered + return runner; + } + }; + + function endFn() { + close(); + } + + function cancelFn() { + close(true); + } + + function close(rejected) { // jshint ignore:line + // if the promise has been called already then we shouldn't close + // the animation again + if (animationClosed || (animationCompleted && animationPaused)) return; + animationClosed = true; + animationPaused = false; + + if (!options.$$skipPreparationClasses) { + $$jqLite.removeClass(element, preparationClasses); + } + $$jqLite.removeClass(element, activeClasses); + + blockKeyframeAnimations(node, false); + blockTransitions(node, false); + + forEach(temporaryStyles, function(entry) { + // There is only one way to remove inline style properties entirely from elements. + // By using `removeProperty` this works, but we need to convert camel-cased CSS + // styles down to hyphenated values. + node.style[entry[0]] = ''; + }); + + applyAnimationClasses(element, options); + applyAnimationStyles(element, options); + + if (Object.keys(restoreStyles).length) { + forEach(restoreStyles, function(value, prop) { + value ? node.style.setProperty(prop, value) + : node.style.removeProperty(prop); + }); + } + + // the reason why we have this option is to allow a synchronous closing callback + // that is fired as SOON as the animation ends (when the CSS is removed) or if + // the animation never takes off at all. A good example is a leave animation since + // the element must be removed just after the animation is over or else the element + // will appear on screen for one animation frame causing an overbearing flicker. + if (options.onDone) { + options.onDone(); + } + + if (events && events.length) { + // Remove the transitionend / animationend listener(s) + element.off(events.join(' '), onAnimationProgress); + } + + //Cancel the fallback closing timeout and remove the timer data + var animationTimerData = element.data(ANIMATE_TIMER_KEY); + if (animationTimerData) { + $timeout.cancel(animationTimerData[0].timer); + element.removeData(ANIMATE_TIMER_KEY); + } + + // if the preparation function fails then the promise is not setup + if (runner) { + runner.complete(!rejected); + } + } + + function applyBlocking(duration) { + if (flags.blockTransition) { + blockTransitions(node, duration); + } + + if (flags.blockKeyframeAnimation) { + blockKeyframeAnimations(node, !!duration); + } + } + + function closeAndReturnNoopAnimator() { + runner = new $$AnimateRunner({ + end: endFn, + cancel: cancelFn + }); + + // should flush the cache animation + waitUntilQuiet(noop); + close(); + + return { + $$willAnimate: false, + start: function() { + return runner; + }, + end: endFn + }; + } + + function onAnimationProgress(event) { + event.stopPropagation(); + var ev = event.originalEvent || event; + + // we now always use `Date.now()` due to the recent changes with + // event.timeStamp in Firefox, Webkit and Chrome (see #13494 for more info) + var timeStamp = ev.$manualTimeStamp || Date.now(); + + /* Firefox (or possibly just Gecko) likes to not round values up + * when a ms measurement is used for the animation */ + var elapsedTime = parseFloat(ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES)); + + /* $manualTimeStamp is a mocked timeStamp value which is set + * within browserTrigger(). This is only here so that tests can + * mock animations properly. Real events fallback to event.timeStamp, + * or, if they don't, then a timeStamp is automatically created for them. + * We're checking to see if the timeStamp surpasses the expected delay, + * but we're using elapsedTime instead of the timeStamp on the 2nd + * pre-condition since animationPauseds sometimes close off early */ + if (Math.max(timeStamp - startTime, 0) >= maxDelayTime && elapsedTime >= maxDuration) { + // we set this flag to ensure that if the transition is paused then, when resumed, + // the animation will automatically close itself since transitions cannot be paused. + animationCompleted = true; + close(); + } + } + + function start() { + if (animationClosed) return; + if (!node.parentNode) { + close(); + return; + } + + // even though we only pause keyframe animations here the pause flag + // will still happen when transitions are used. Only the transition will + // not be paused since that is not possible. If the animation ends when + // paused then it will not complete until unpaused or cancelled. + var playPause = function(playAnimation) { + if (!animationCompleted) { + animationPaused = !playAnimation; + if (timings.animationDuration) { + var value = blockKeyframeAnimations(node, animationPaused); + animationPaused + ? temporaryStyles.push(value) + : removeFromArray(temporaryStyles, value); + } + } else if (animationPaused && playAnimation) { + animationPaused = false; + close(); + } + }; + + // checking the stagger duration prevents an accidentally cascade of the CSS delay style + // being inherited from the parent. If the transition duration is zero then we can safely + // rely that the delay value is an intentional stagger delay style. + var maxStagger = itemIndex > 0 + && ((timings.transitionDuration && stagger.transitionDuration === 0) || + (timings.animationDuration && stagger.animationDuration === 0)) + && Math.max(stagger.animationDelay, stagger.transitionDelay); + if (maxStagger) { + $timeout(triggerAnimationStart, + Math.floor(maxStagger * itemIndex * ONE_SECOND), + false); + } else { + triggerAnimationStart(); + } + + // this will decorate the existing promise runner with pause/resume methods + runnerHost.resume = function() { + playPause(true); + }; + + runnerHost.pause = function() { + playPause(false); + }; + + function triggerAnimationStart() { + // just incase a stagger animation kicks in when the animation + // itself was cancelled entirely + if (animationClosed) return; + + applyBlocking(false); + + forEach(temporaryStyles, function(entry) { + var key = entry[0]; + var value = entry[1]; + node.style[key] = value; + }); + + applyAnimationClasses(element, options); + $$jqLite.addClass(element, activeClasses); + + if (flags.recalculateTimingStyles) { + fullClassName = node.className + ' ' + preparationClasses; + cacheKey = gcsHashFn(node, fullClassName); + + timings = computeTimings(node, fullClassName, cacheKey); + relativeDelay = timings.maxDelay; + maxDelay = Math.max(relativeDelay, 0); + maxDuration = timings.maxDuration; + + if (maxDuration === 0) { + close(); + return; + } + + flags.hasTransitions = timings.transitionDuration > 0; + flags.hasAnimations = timings.animationDuration > 0; + } + + if (flags.applyAnimationDelay) { + relativeDelay = typeof options.delay !== "boolean" && truthyTimingValue(options.delay) + ? parseFloat(options.delay) + : relativeDelay; + + maxDelay = Math.max(relativeDelay, 0); + timings.animationDelay = relativeDelay; + delayStyle = getCssDelayStyle(relativeDelay, true); + temporaryStyles.push(delayStyle); + node.style[delayStyle[0]] = delayStyle[1]; + } + + maxDelayTime = maxDelay * ONE_SECOND; + maxDurationTime = maxDuration * ONE_SECOND; + + if (options.easing) { + var easeProp, easeVal = options.easing; + if (flags.hasTransitions) { + easeProp = TRANSITION_PROP + TIMING_KEY; + temporaryStyles.push([easeProp, easeVal]); + node.style[easeProp] = easeVal; + } + if (flags.hasAnimations) { + easeProp = ANIMATION_PROP + TIMING_KEY; + temporaryStyles.push([easeProp, easeVal]); + node.style[easeProp] = easeVal; + } + } + + if (timings.transitionDuration) { + events.push(TRANSITIONEND_EVENT); + } + + if (timings.animationDuration) { + events.push(ANIMATIONEND_EVENT); + } + + startTime = Date.now(); + var timerTime = maxDelayTime + CLOSING_TIME_BUFFER * maxDurationTime; + var endTime = startTime + timerTime; + + var animationsData = element.data(ANIMATE_TIMER_KEY) || []; + var setupFallbackTimer = true; + if (animationsData.length) { + var currentTimerData = animationsData[0]; + setupFallbackTimer = endTime > currentTimerData.expectedEndTime; + if (setupFallbackTimer) { + $timeout.cancel(currentTimerData.timer); + } else { + animationsData.push(close); + } + } + + if (setupFallbackTimer) { + var timer = $timeout(onAnimationExpired, timerTime, false); + animationsData[0] = { + timer: timer, + expectedEndTime: endTime + }; + animationsData.push(close); + element.data(ANIMATE_TIMER_KEY, animationsData); + } + + if (events.length) { + element.on(events.join(' '), onAnimationProgress); + } + + if (options.to) { + if (options.cleanupStyles) { + registerRestorableStyles(restoreStyles, node, Object.keys(options.to)); + } + applyAnimationToStyles(element, options); + } + } + + function onAnimationExpired() { + var animationsData = element.data(ANIMATE_TIMER_KEY); + + // this will be false in the event that the element was + // removed from the DOM (via a leave animation or something + // similar) + if (animationsData) { + for (var i = 1; i < animationsData.length; i++) { + animationsData[i](); + } + element.removeData(ANIMATE_TIMER_KEY); + } + } + } + }; + }]; +}]; + +var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationProvider) { + $$animationProvider.drivers.push('$$animateCssDriver'); + + var NG_ANIMATE_SHIM_CLASS_NAME = 'ng-animate-shim'; + var NG_ANIMATE_ANCHOR_CLASS_NAME = 'ng-anchor'; + + var NG_OUT_ANCHOR_CLASS_NAME = 'ng-anchor-out'; + var NG_IN_ANCHOR_CLASS_NAME = 'ng-anchor-in'; + + function isDocumentFragment(node) { + return node.parentNode && node.parentNode.nodeType === 11; + } + + this.$get = ['$animateCss', '$rootScope', '$$AnimateRunner', '$rootElement', '$sniffer', '$$jqLite', '$document', + function($animateCss, $rootScope, $$AnimateRunner, $rootElement, $sniffer, $$jqLite, $document) { + + // only browsers that support these properties can render animations + if (!$sniffer.animations && !$sniffer.transitions) return noop; + + var bodyNode = $document[0].body; + var rootNode = getDomNode($rootElement); + + var rootBodyElement = jqLite( + // this is to avoid using something that exists outside of the body + // we also special case the doc fragment case because our unit test code + // appends the $rootElement to the body after the app has been bootstrapped + isDocumentFragment(rootNode) || bodyNode.contains(rootNode) ? rootNode : bodyNode + ); + + var applyAnimationClasses = applyAnimationClassesFactory($$jqLite); + + return function initDriverFn(animationDetails) { + return animationDetails.from && animationDetails.to + ? prepareFromToAnchorAnimation(animationDetails.from, + animationDetails.to, + animationDetails.classes, + animationDetails.anchors) + : prepareRegularAnimation(animationDetails); + }; + + function filterCssClasses(classes) { + //remove all the `ng-` stuff + return classes.replace(/\bng-\S+\b/g, ''); + } + + function getUniqueValues(a, b) { + if (isString(a)) a = a.split(' '); + if (isString(b)) b = b.split(' '); + return a.filter(function(val) { + return b.indexOf(val) === -1; + }).join(' '); + } + + function prepareAnchoredAnimation(classes, outAnchor, inAnchor) { + var clone = jqLite(getDomNode(outAnchor).cloneNode(true)); + var startingClasses = filterCssClasses(getClassVal(clone)); + + outAnchor.addClass(NG_ANIMATE_SHIM_CLASS_NAME); + inAnchor.addClass(NG_ANIMATE_SHIM_CLASS_NAME); + + clone.addClass(NG_ANIMATE_ANCHOR_CLASS_NAME); + + rootBodyElement.append(clone); + + var animatorIn, animatorOut = prepareOutAnimation(); + + // the user may not end up using the `out` animation and + // only making use of the `in` animation or vice-versa. + // In either case we should allow this and not assume the + // animation is over unless both animations are not used. + if (!animatorOut) { + animatorIn = prepareInAnimation(); + if (!animatorIn) { + return end(); + } + } + + var startingAnimator = animatorOut || animatorIn; + + return { + start: function() { + var runner; + + var currentAnimation = startingAnimator.start(); + currentAnimation.done(function() { + currentAnimation = null; + if (!animatorIn) { + animatorIn = prepareInAnimation(); + if (animatorIn) { + currentAnimation = animatorIn.start(); + currentAnimation.done(function() { + currentAnimation = null; + end(); + runner.complete(); + }); + return currentAnimation; + } + } + // in the event that there is no `in` animation + end(); + runner.complete(); + }); + + runner = new $$AnimateRunner({ + end: endFn, + cancel: endFn + }); + + return runner; + + function endFn() { + if (currentAnimation) { + currentAnimation.end(); + } + } + } + }; + + function calculateAnchorStyles(anchor) { + var styles = {}; + + var coords = getDomNode(anchor).getBoundingClientRect(); + + // we iterate directly since safari messes up and doesn't return + // all the keys for the coords object when iterated + forEach(['width','height','top','left'], function(key) { + var value = coords[key]; + switch (key) { + case 'top': + value += bodyNode.scrollTop; + break; + case 'left': + value += bodyNode.scrollLeft; + break; + } + styles[key] = Math.floor(value) + 'px'; + }); + return styles; + } + + function prepareOutAnimation() { + var animator = $animateCss(clone, { + addClass: NG_OUT_ANCHOR_CLASS_NAME, + delay: true, + from: calculateAnchorStyles(outAnchor) + }); + + // read the comment within `prepareRegularAnimation` to understand + // why this check is necessary + return animator.$$willAnimate ? animator : null; + } + + function getClassVal(element) { + return element.attr('class') || ''; + } + + function prepareInAnimation() { + var endingClasses = filterCssClasses(getClassVal(inAnchor)); + var toAdd = getUniqueValues(endingClasses, startingClasses); + var toRemove = getUniqueValues(startingClasses, endingClasses); + + var animator = $animateCss(clone, { + to: calculateAnchorStyles(inAnchor), + addClass: NG_IN_ANCHOR_CLASS_NAME + ' ' + toAdd, + removeClass: NG_OUT_ANCHOR_CLASS_NAME + ' ' + toRemove, + delay: true + }); + + // read the comment within `prepareRegularAnimation` to understand + // why this check is necessary + return animator.$$willAnimate ? animator : null; + } + + function end() { + clone.remove(); + outAnchor.removeClass(NG_ANIMATE_SHIM_CLASS_NAME); + inAnchor.removeClass(NG_ANIMATE_SHIM_CLASS_NAME); + } + } + + function prepareFromToAnchorAnimation(from, to, classes, anchors) { + var fromAnimation = prepareRegularAnimation(from, noop); + var toAnimation = prepareRegularAnimation(to, noop); + + var anchorAnimations = []; + forEach(anchors, function(anchor) { + var outElement = anchor['out']; + var inElement = anchor['in']; + var animator = prepareAnchoredAnimation(classes, outElement, inElement); + if (animator) { + anchorAnimations.push(animator); + } + }); + + // no point in doing anything when there are no elements to animate + if (!fromAnimation && !toAnimation && anchorAnimations.length === 0) return; + + return { + start: function() { + var animationRunners = []; + + if (fromAnimation) { + animationRunners.push(fromAnimation.start()); + } + + if (toAnimation) { + animationRunners.push(toAnimation.start()); + } + + forEach(anchorAnimations, function(animation) { + animationRunners.push(animation.start()); + }); + + var runner = new $$AnimateRunner({ + end: endFn, + cancel: endFn // CSS-driven animations cannot be cancelled, only ended + }); + + $$AnimateRunner.all(animationRunners, function(status) { + runner.complete(status); + }); + + return runner; + + function endFn() { + forEach(animationRunners, function(runner) { + runner.end(); + }); + } + } + }; + } + + function prepareRegularAnimation(animationDetails) { + var element = animationDetails.element; + var options = animationDetails.options || {}; + + if (animationDetails.structural) { + options.event = animationDetails.event; + options.structural = true; + options.applyClassesEarly = true; + + // we special case the leave animation since we want to ensure that + // the element is removed as soon as the animation is over. Otherwise + // a flicker might appear or the element may not be removed at all + if (animationDetails.event === 'leave') { + options.onDone = options.domOperation; + } + } + + // We assign the preparationClasses as the actual animation event since + // the internals of $animateCss will just suffix the event token values + // with `-active` to trigger the animation. + if (options.preparationClasses) { + options.event = concatWithSpace(options.event, options.preparationClasses); + } + + var animator = $animateCss(element, options); + + // the driver lookup code inside of $$animation attempts to spawn a + // driver one by one until a driver returns a.$$willAnimate animator object. + // $animateCss will always return an object, however, it will pass in + // a flag as a hint as to whether an animation was detected or not + return animator.$$willAnimate ? animator : null; + } + }]; +}]; + +// TODO(matsko): use caching here to speed things up for detection +// TODO(matsko): add documentation +// by the time... + +var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) { + this.$get = ['$injector', '$$AnimateRunner', '$$jqLite', + function($injector, $$AnimateRunner, $$jqLite) { + + var applyAnimationClasses = applyAnimationClassesFactory($$jqLite); + // $animateJs(element, 'enter'); + return function(element, event, classes, options) { + var animationClosed = false; + + // the `classes` argument is optional and if it is not used + // then the classes will be resolved from the element's className + // property as well as options.addClass/options.removeClass. + if (arguments.length === 3 && isObject(classes)) { + options = classes; + classes = null; + } + + options = prepareAnimationOptions(options); + if (!classes) { + classes = element.attr('class') || ''; + if (options.addClass) { + classes += ' ' + options.addClass; + } + if (options.removeClass) { + classes += ' ' + options.removeClass; + } + } + + var classesToAdd = options.addClass; + var classesToRemove = options.removeClass; + + // the lookupAnimations function returns a series of animation objects that are + // matched up with one or more of the CSS classes. These animation objects are + // defined via the module.animation factory function. If nothing is detected then + // we don't return anything which then makes $animation query the next driver. + var animations = lookupAnimations(classes); + var before, after; + if (animations.length) { + var afterFn, beforeFn; + if (event == 'leave') { + beforeFn = 'leave'; + afterFn = 'afterLeave'; // TODO(matsko): get rid of this + } else { + beforeFn = 'before' + event.charAt(0).toUpperCase() + event.substr(1); + afterFn = event; + } + + if (event !== 'enter' && event !== 'move') { + before = packageAnimations(element, event, options, animations, beforeFn); + } + after = packageAnimations(element, event, options, animations, afterFn); + } + + // no matching animations + if (!before && !after) return; + + function applyOptions() { + options.domOperation(); + applyAnimationClasses(element, options); + } + + function close() { + animationClosed = true; + applyOptions(); + applyAnimationStyles(element, options); + } + + var runner; + + return { + $$willAnimate: true, + end: function() { + if (runner) { + runner.end(); + } else { + close(); + runner = new $$AnimateRunner(); + runner.complete(true); + } + return runner; + }, + start: function() { + if (runner) { + return runner; + } + + runner = new $$AnimateRunner(); + var closeActiveAnimations; + var chain = []; + + if (before) { + chain.push(function(fn) { + closeActiveAnimations = before(fn); + }); + } + + if (chain.length) { + chain.push(function(fn) { + applyOptions(); + fn(true); + }); + } else { + applyOptions(); + } + + if (after) { + chain.push(function(fn) { + closeActiveAnimations = after(fn); + }); + } + + runner.setHost({ + end: function() { + endAnimations(); + }, + cancel: function() { + endAnimations(true); + } + }); + + $$AnimateRunner.chain(chain, onComplete); + return runner; + + function onComplete(success) { + close(success); + runner.complete(success); + } + + function endAnimations(cancelled) { + if (!animationClosed) { + (closeActiveAnimations || noop)(cancelled); + onComplete(cancelled); + } + } + } + }; + + function executeAnimationFn(fn, element, event, options, onDone) { + var args; + switch (event) { + case 'animate': + args = [element, options.from, options.to, onDone]; + break; + + case 'setClass': + args = [element, classesToAdd, classesToRemove, onDone]; + break; + + case 'addClass': + args = [element, classesToAdd, onDone]; + break; + + case 'removeClass': + args = [element, classesToRemove, onDone]; + break; + + default: + args = [element, onDone]; + break; + } + + args.push(options); + + var value = fn.apply(fn, args); + if (value) { + if (isFunction(value.start)) { + value = value.start(); + } + + if (value instanceof $$AnimateRunner) { + value.done(onDone); + } else if (isFunction(value)) { + // optional onEnd / onCancel callback + return value; + } + } + + return noop; + } + + function groupEventedAnimations(element, event, options, animations, fnName) { + var operations = []; + forEach(animations, function(ani) { + var animation = ani[fnName]; + if (!animation) return; + + // note that all of these animations will run in parallel + operations.push(function() { + var runner; + var endProgressCb; + + var resolved = false; + var onAnimationComplete = function(rejected) { + if (!resolved) { + resolved = true; + (endProgressCb || noop)(rejected); + runner.complete(!rejected); + } + }; + + runner = new $$AnimateRunner({ + end: function() { + onAnimationComplete(); + }, + cancel: function() { + onAnimationComplete(true); + } + }); + + endProgressCb = executeAnimationFn(animation, element, event, options, function(result) { + var cancelled = result === false; + onAnimationComplete(cancelled); + }); + + return runner; + }); + }); + + return operations; + } + + function packageAnimations(element, event, options, animations, fnName) { + var operations = groupEventedAnimations(element, event, options, animations, fnName); + if (operations.length === 0) { + var a,b; + if (fnName === 'beforeSetClass') { + a = groupEventedAnimations(element, 'removeClass', options, animations, 'beforeRemoveClass'); + b = groupEventedAnimations(element, 'addClass', options, animations, 'beforeAddClass'); + } else if (fnName === 'setClass') { + a = groupEventedAnimations(element, 'removeClass', options, animations, 'removeClass'); + b = groupEventedAnimations(element, 'addClass', options, animations, 'addClass'); + } + + if (a) { + operations = operations.concat(a); + } + if (b) { + operations = operations.concat(b); + } + } + + if (operations.length === 0) return; + + // TODO(matsko): add documentation + return function startAnimation(callback) { + var runners = []; + if (operations.length) { + forEach(operations, function(animateFn) { + runners.push(animateFn()); + }); + } + + runners.length ? $$AnimateRunner.all(runners, callback) : callback(); + + return function endFn(reject) { + forEach(runners, function(runner) { + reject ? runner.cancel() : runner.end(); + }); + }; + }; + } + }; + + function lookupAnimations(classes) { + classes = isArray(classes) ? classes : classes.split(' '); + var matches = [], flagMap = {}; + for (var i=0; i < classes.length; i++) { + var klass = classes[i], + animationFactory = $animateProvider.$$registeredAnimations[klass]; + if (animationFactory && !flagMap[klass]) { + matches.push($injector.get(animationFactory)); + flagMap[klass] = true; + } + } + return matches; + } + }]; +}]; + +var $$AnimateJsDriverProvider = ['$$animationProvider', function($$animationProvider) { + $$animationProvider.drivers.push('$$animateJsDriver'); + this.$get = ['$$animateJs', '$$AnimateRunner', function($$animateJs, $$AnimateRunner) { + return function initDriverFn(animationDetails) { + if (animationDetails.from && animationDetails.to) { + var fromAnimation = prepareAnimation(animationDetails.from); + var toAnimation = prepareAnimation(animationDetails.to); + if (!fromAnimation && !toAnimation) return; + + return { + start: function() { + var animationRunners = []; + + if (fromAnimation) { + animationRunners.push(fromAnimation.start()); + } + + if (toAnimation) { + animationRunners.push(toAnimation.start()); + } + + $$AnimateRunner.all(animationRunners, done); + + var runner = new $$AnimateRunner({ + end: endFnFactory(), + cancel: endFnFactory() + }); + + return runner; + + function endFnFactory() { + return function() { + forEach(animationRunners, function(runner) { + // at this point we cannot cancel animations for groups just yet. 1.5+ + runner.end(); + }); + }; + } + + function done(status) { + runner.complete(status); + } + } + }; + } else { + return prepareAnimation(animationDetails); + } + }; + + function prepareAnimation(animationDetails) { + // TODO(matsko): make sure to check for grouped animations and delegate down to normal animations + var element = animationDetails.element; + var event = animationDetails.event; + var options = animationDetails.options; + var classes = animationDetails.classes; + return $$animateJs(element, event, classes, options); + } + }]; +}]; + +var NG_ANIMATE_ATTR_NAME = 'data-ng-animate'; +var NG_ANIMATE_PIN_DATA = '$ngAnimatePin'; +var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) { + var PRE_DIGEST_STATE = 1; + var RUNNING_STATE = 2; + var ONE_SPACE = ' '; + + var rules = this.rules = { + skip: [], + cancel: [], + join: [] + }; + + function makeTruthyCssClassMap(classString) { + if (!classString) { + return null; + } + + var keys = classString.split(ONE_SPACE); + var map = Object.create(null); + + forEach(keys, function(key) { + map[key] = true; + }); + return map; + } + + function hasMatchingClasses(newClassString, currentClassString) { + if (newClassString && currentClassString) { + var currentClassMap = makeTruthyCssClassMap(currentClassString); + return newClassString.split(ONE_SPACE).some(function(className) { + return currentClassMap[className]; + }); + } + } + + function isAllowed(ruleType, element, currentAnimation, previousAnimation) { + return rules[ruleType].some(function(fn) { + return fn(element, currentAnimation, previousAnimation); + }); + } + + function hasAnimationClasses(animation, and) { + var a = (animation.addClass || '').length > 0; + var b = (animation.removeClass || '').length > 0; + return and ? a && b : a || b; + } + + rules.join.push(function(element, newAnimation, currentAnimation) { + // if the new animation is class-based then we can just tack that on + return !newAnimation.structural && hasAnimationClasses(newAnimation); + }); + + rules.skip.push(function(element, newAnimation, currentAnimation) { + // there is no need to animate anything if no classes are being added and + // there is no structural animation that will be triggered + return !newAnimation.structural && !hasAnimationClasses(newAnimation); + }); + + rules.skip.push(function(element, newAnimation, currentAnimation) { + // why should we trigger a new structural animation if the element will + // be removed from the DOM anyway? + return currentAnimation.event == 'leave' && newAnimation.structural; + }); + + rules.skip.push(function(element, newAnimation, currentAnimation) { + // if there is an ongoing current animation then don't even bother running the class-based animation + return currentAnimation.structural && currentAnimation.state === RUNNING_STATE && !newAnimation.structural; + }); + + rules.cancel.push(function(element, newAnimation, currentAnimation) { + // there can never be two structural animations running at the same time + return currentAnimation.structural && newAnimation.structural; + }); + + rules.cancel.push(function(element, newAnimation, currentAnimation) { + // if the previous animation is already running, but the new animation will + // be triggered, but the new animation is structural + return currentAnimation.state === RUNNING_STATE && newAnimation.structural; + }); + + rules.cancel.push(function(element, newAnimation, currentAnimation) { + // cancel the animation if classes added / removed in both animation cancel each other out, + // but only if the current animation isn't structural + + if (currentAnimation.structural) return false; + + var nA = newAnimation.addClass; + var nR = newAnimation.removeClass; + var cA = currentAnimation.addClass; + var cR = currentAnimation.removeClass; + + // early detection to save the global CPU shortage :) + if ((isUndefined(nA) && isUndefined(nR)) || (isUndefined(cA) && isUndefined(cR))) { + return false; + } + + return hasMatchingClasses(nA, cR) || hasMatchingClasses(nR, cA); + }); + + this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$HashMap', + '$$animation', '$$AnimateRunner', '$templateRequest', '$$jqLite', '$$forceReflow', + function($$rAF, $rootScope, $rootElement, $document, $$HashMap, + $$animation, $$AnimateRunner, $templateRequest, $$jqLite, $$forceReflow) { + + var activeAnimationsLookup = new $$HashMap(); + var disabledElementsLookup = new $$HashMap(); + var animationsEnabled = null; + + function postDigestTaskFactory() { + var postDigestCalled = false; + return function(fn) { + // we only issue a call to postDigest before + // it has first passed. This prevents any callbacks + // from not firing once the animation has completed + // since it will be out of the digest cycle. + if (postDigestCalled) { + fn(); + } else { + $rootScope.$$postDigest(function() { + postDigestCalled = true; + fn(); + }); + } + }; + } + + // Wait until all directive and route-related templates are downloaded and + // compiled. The $templateRequest.totalPendingRequests variable keeps track of + // all of the remote templates being currently downloaded. If there are no + // templates currently downloading then the watcher will still fire anyway. + var deregisterWatch = $rootScope.$watch( + function() { return $templateRequest.totalPendingRequests === 0; }, + function(isEmpty) { + if (!isEmpty) return; + deregisterWatch(); + + // Now that all templates have been downloaded, $animate will wait until + // the post digest queue is empty before enabling animations. By having two + // calls to $postDigest calls we can ensure that the flag is enabled at the + // very end of the post digest queue. Since all of the animations in $animate + // use $postDigest, it's important that the code below executes at the end. + // This basically means that the page is fully downloaded and compiled before + // any animations are triggered. + $rootScope.$$postDigest(function() { + $rootScope.$$postDigest(function() { + // we check for null directly in the event that the application already called + // .enabled() with whatever arguments that it provided it with + if (animationsEnabled === null) { + animationsEnabled = true; + } + }); + }); + } + ); + + var callbackRegistry = {}; + + // remember that the classNameFilter is set during the provider/config + // stage therefore we can optimize here and setup a helper function + var classNameFilter = $animateProvider.classNameFilter(); + var isAnimatableClassName = !classNameFilter + ? function() { return true; } + : function(className) { + return classNameFilter.test(className); + }; + + var applyAnimationClasses = applyAnimationClassesFactory($$jqLite); + + function normalizeAnimationDetails(element, animation) { + return mergeAnimationDetails(element, animation, {}); + } + + // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259. + var contains = Node.prototype.contains || function(arg) { + // jshint bitwise: false + return this === arg || !!(this.compareDocumentPosition(arg) & 16); + // jshint bitwise: true + }; + + function findCallbacks(parent, element, event) { + var targetNode = getDomNode(element); + var targetParentNode = getDomNode(parent); + + var matches = []; + var entries = callbackRegistry[event]; + if (entries) { + forEach(entries, function(entry) { + if (contains.call(entry.node, targetNode)) { + matches.push(entry.callback); + } else if (event === 'leave' && contains.call(entry.node, targetParentNode)) { + matches.push(entry.callback); + } + }); + } + + return matches; + } + + var $animate = { + on: function(event, container, callback) { + var node = extractElementNode(container); + callbackRegistry[event] = callbackRegistry[event] || []; + callbackRegistry[event].push({ + node: node, + callback: callback + }); + + // Remove the callback when the element is removed from the DOM + jqLite(container).on('$destroy', function() { + $animate.off(event, container, callback); + }); + }, + + off: function(event, container, callback) { + var entries = callbackRegistry[event]; + if (!entries) return; + + callbackRegistry[event] = arguments.length === 1 + ? null + : filterFromRegistry(entries, container, callback); + + function filterFromRegistry(list, matchContainer, matchCallback) { + var containerNode = extractElementNode(matchContainer); + return list.filter(function(entry) { + var isMatch = entry.node === containerNode && + (!matchCallback || entry.callback === matchCallback); + return !isMatch; + }); + } + }, + + pin: function(element, parentElement) { + assertArg(isElement(element), 'element', 'not an element'); + assertArg(isElement(parentElement), 'parentElement', 'not an element'); + element.data(NG_ANIMATE_PIN_DATA, parentElement); + }, + + push: function(element, event, options, domOperation) { + options = options || {}; + options.domOperation = domOperation; + return queueAnimation(element, event, options); + }, + + // this method has four signatures: + // () - global getter + // (bool) - global setter + // (element) - element getter + // (element, bool) - element setter + enabled: function(element, bool) { + var argCount = arguments.length; + + if (argCount === 0) { + // () - Global getter + bool = !!animationsEnabled; + } else { + var hasElement = isElement(element); + + if (!hasElement) { + // (bool) - Global setter + bool = animationsEnabled = !!element; + } else { + var node = getDomNode(element); + var recordExists = disabledElementsLookup.get(node); + + if (argCount === 1) { + // (element) - Element getter + bool = !recordExists; + } else { + // (element, bool) - Element setter + disabledElementsLookup.put(node, !bool); + } + } + } + + return bool; + } + }; + + return $animate; + + function queueAnimation(element, event, initialOptions) { + // we always make a copy of the options since + // there should never be any side effects on + // the input data when running `$animateCss`. + var options = copy(initialOptions); + + var node, parent; + element = stripCommentsFromElement(element); + if (element) { + node = getDomNode(element); + parent = element.parent(); + } + + options = prepareAnimationOptions(options); + + // we create a fake runner with a working promise. + // These methods will become available after the digest has passed + var runner = new $$AnimateRunner(); + + // this is used to trigger callbacks in postDigest mode + var runInNextPostDigestOrNow = postDigestTaskFactory(); + + if (isArray(options.addClass)) { + options.addClass = options.addClass.join(' '); + } + + if (options.addClass && !isString(options.addClass)) { + options.addClass = null; + } + + if (isArray(options.removeClass)) { + options.removeClass = options.removeClass.join(' '); + } + + if (options.removeClass && !isString(options.removeClass)) { + options.removeClass = null; + } + + if (options.from && !isObject(options.from)) { + options.from = null; + } + + if (options.to && !isObject(options.to)) { + options.to = null; + } + + // there are situations where a directive issues an animation for + // a jqLite wrapper that contains only comment nodes... If this + // happens then there is no way we can perform an animation + if (!node) { + close(); + return runner; + } + + var className = [node.className, options.addClass, options.removeClass].join(' '); + if (!isAnimatableClassName(className)) { + close(); + return runner; + } + + var isStructural = ['enter', 'move', 'leave'].indexOf(event) >= 0; + + // this is a hard disable of all animations for the application or on + // the element itself, therefore there is no need to continue further + // past this point if not enabled + // Animations are also disabled if the document is currently hidden (page is not visible + // to the user), because browsers slow down or do not flush calls to requestAnimationFrame + var skipAnimations = !animationsEnabled || $document[0].hidden || disabledElementsLookup.get(node); + var existingAnimation = (!skipAnimations && activeAnimationsLookup.get(node)) || {}; + var hasExistingAnimation = !!existingAnimation.state; + + // there is no point in traversing the same collection of parent ancestors if a followup + // animation will be run on the same element that already did all that checking work + if (!skipAnimations && (!hasExistingAnimation || existingAnimation.state != PRE_DIGEST_STATE)) { + skipAnimations = !areAnimationsAllowed(element, parent, event); + } + + if (skipAnimations) { + close(); + return runner; + } + + if (isStructural) { + closeChildAnimations(element); + } + + var newAnimation = { + structural: isStructural, + element: element, + event: event, + addClass: options.addClass, + removeClass: options.removeClass, + close: close, + options: options, + runner: runner + }; + + if (hasExistingAnimation) { + var skipAnimationFlag = isAllowed('skip', element, newAnimation, existingAnimation); + if (skipAnimationFlag) { + if (existingAnimation.state === RUNNING_STATE) { + close(); + return runner; + } else { + mergeAnimationDetails(element, existingAnimation, newAnimation); + return existingAnimation.runner; + } + } + var cancelAnimationFlag = isAllowed('cancel', element, newAnimation, existingAnimation); + if (cancelAnimationFlag) { + if (existingAnimation.state === RUNNING_STATE) { + // this will end the animation right away and it is safe + // to do so since the animation is already running and the + // runner callback code will run in async + existingAnimation.runner.end(); + } else if (existingAnimation.structural) { + // this means that the animation is queued into a digest, but + // hasn't started yet. Therefore it is safe to run the close + // method which will call the runner methods in async. + existingAnimation.close(); + } else { + // this will merge the new animation options into existing animation options + mergeAnimationDetails(element, existingAnimation, newAnimation); + + return existingAnimation.runner; + } + } else { + // a joined animation means that this animation will take over the existing one + // so an example would involve a leave animation taking over an enter. Then when + // the postDigest kicks in the enter will be ignored. + var joinAnimationFlag = isAllowed('join', element, newAnimation, existingAnimation); + if (joinAnimationFlag) { + if (existingAnimation.state === RUNNING_STATE) { + normalizeAnimationDetails(element, newAnimation); + } else { + applyGeneratedPreparationClasses(element, isStructural ? event : null, options); + + event = newAnimation.event = existingAnimation.event; + options = mergeAnimationDetails(element, existingAnimation, newAnimation); + + //we return the same runner since only the option values of this animation will + //be fed into the `existingAnimation`. + return existingAnimation.runner; + } + } + } + } else { + // normalization in this case means that it removes redundant CSS classes that + // already exist (addClass) or do not exist (removeClass) on the element + normalizeAnimationDetails(element, newAnimation); + } + + // when the options are merged and cleaned up we may end up not having to do + // an animation at all, therefore we should check this before issuing a post + // digest callback. Structural animations will always run no matter what. + var isValidAnimation = newAnimation.structural; + if (!isValidAnimation) { + // animate (from/to) can be quickly checked first, otherwise we check if any classes are present + isValidAnimation = (newAnimation.event === 'animate' && Object.keys(newAnimation.options.to || {}).length > 0) + || hasAnimationClasses(newAnimation); + } + + if (!isValidAnimation) { + close(); + clearElementAnimationState(element); + return runner; + } + + // the counter keeps track of cancelled animations + var counter = (existingAnimation.counter || 0) + 1; + newAnimation.counter = counter; + + markElementAnimationState(element, PRE_DIGEST_STATE, newAnimation); + + $rootScope.$$postDigest(function() { + var animationDetails = activeAnimationsLookup.get(node); + var animationCancelled = !animationDetails; + animationDetails = animationDetails || {}; + + // if addClass/removeClass is called before something like enter then the + // registered parent element may not be present. The code below will ensure + // that a final value for parent element is obtained + var parentElement = element.parent() || []; + + // animate/structural/class-based animations all have requirements. Otherwise there + // is no point in performing an animation. The parent node must also be set. + var isValidAnimation = parentElement.length > 0 + && (animationDetails.event === 'animate' + || animationDetails.structural + || hasAnimationClasses(animationDetails)); + + // this means that the previous animation was cancelled + // even if the follow-up animation is the same event + if (animationCancelled || animationDetails.counter !== counter || !isValidAnimation) { + // if another animation did not take over then we need + // to make sure that the domOperation and options are + // handled accordingly + if (animationCancelled) { + applyAnimationClasses(element, options); + applyAnimationStyles(element, options); + } + + // if the event changed from something like enter to leave then we do + // it, otherwise if it's the same then the end result will be the same too + if (animationCancelled || (isStructural && animationDetails.event !== event)) { + options.domOperation(); + runner.end(); + } + + // in the event that the element animation was not cancelled or a follow-up animation + // isn't allowed to animate from here then we need to clear the state of the element + // so that any future animations won't read the expired animation data. + if (!isValidAnimation) { + clearElementAnimationState(element); + } + + return; + } + + // this combined multiple class to addClass / removeClass into a setClass event + // so long as a structural event did not take over the animation + event = !animationDetails.structural && hasAnimationClasses(animationDetails, true) + ? 'setClass' + : animationDetails.event; + + markElementAnimationState(element, RUNNING_STATE); + var realRunner = $$animation(element, event, animationDetails.options); + + realRunner.done(function(status) { + close(!status); + var animationDetails = activeAnimationsLookup.get(node); + if (animationDetails && animationDetails.counter === counter) { + clearElementAnimationState(getDomNode(element)); + } + notifyProgress(runner, event, 'close', {}); + }); + + // this will update the runner's flow-control events based on + // the `realRunner` object. + runner.setHost(realRunner); + notifyProgress(runner, event, 'start', {}); + }); + + return runner; + + function notifyProgress(runner, event, phase, data) { + runInNextPostDigestOrNow(function() { + var callbacks = findCallbacks(parent, element, event); + if (callbacks.length) { + // do not optimize this call here to RAF because + // we don't know how heavy the callback code here will + // be and if this code is buffered then this can + // lead to a performance regression. + $$rAF(function() { + forEach(callbacks, function(callback) { + callback(element, phase, data); + }); + }); + } + }); + runner.progress(event, phase, data); + } + + function close(reject) { // jshint ignore:line + clearGeneratedClasses(element, options); + applyAnimationClasses(element, options); + applyAnimationStyles(element, options); + options.domOperation(); + runner.complete(!reject); + } + } + + function closeChildAnimations(element) { + var node = getDomNode(element); + var children = node.querySelectorAll('[' + NG_ANIMATE_ATTR_NAME + ']'); + forEach(children, function(child) { + var state = parseInt(child.getAttribute(NG_ANIMATE_ATTR_NAME)); + var animationDetails = activeAnimationsLookup.get(child); + if (animationDetails) { + switch (state) { + case RUNNING_STATE: + animationDetails.runner.end(); + /* falls through */ + case PRE_DIGEST_STATE: + activeAnimationsLookup.remove(child); + break; + } + } + }); + } + + function clearElementAnimationState(element) { + var node = getDomNode(element); + node.removeAttribute(NG_ANIMATE_ATTR_NAME); + activeAnimationsLookup.remove(node); + } + + function isMatchingElement(nodeOrElmA, nodeOrElmB) { + return getDomNode(nodeOrElmA) === getDomNode(nodeOrElmB); + } + + /** + * This fn returns false if any of the following is true: + * a) animations on any parent element are disabled, and animations on the element aren't explicitly allowed + * b) a parent element has an ongoing structural animation, and animateChildren is false + * c) the element is not a child of the body + * d) the element is not a child of the $rootElement + */ + function areAnimationsAllowed(element, parentElement, event) { + var bodyElement = jqLite($document[0].body); + var bodyElementDetected = isMatchingElement(element, bodyElement) || element[0].nodeName === 'HTML'; + var rootElementDetected = isMatchingElement(element, $rootElement); + var parentAnimationDetected = false; + var animateChildren; + var elementDisabled = disabledElementsLookup.get(getDomNode(element)); + + var parentHost = jqLite.data(element[0], NG_ANIMATE_PIN_DATA); + if (parentHost) { + parentElement = parentHost; + } + + parentElement = getDomNode(parentElement); + + while (parentElement) { + if (!rootElementDetected) { + // angular doesn't want to attempt to animate elements outside of the application + // therefore we need to ensure that the rootElement is an ancestor of the current element + rootElementDetected = isMatchingElement(parentElement, $rootElement); + } + + if (parentElement.nodeType !== ELEMENT_NODE) { + // no point in inspecting the #document element + break; + } + + var details = activeAnimationsLookup.get(parentElement) || {}; + // either an enter, leave or move animation will commence + // therefore we can't allow any animations to take place + // but if a parent animation is class-based then that's ok + if (!parentAnimationDetected) { + var parentElementDisabled = disabledElementsLookup.get(parentElement); + + if (parentElementDisabled === true && elementDisabled !== false) { + // disable animations if the user hasn't explicitly enabled animations on the + // current element + elementDisabled = true; + // element is disabled via parent element, no need to check anything else + break; + } else if (parentElementDisabled === false) { + elementDisabled = false; + } + parentAnimationDetected = details.structural; + } + + if (isUndefined(animateChildren) || animateChildren === true) { + var value = jqLite.data(parentElement, NG_ANIMATE_CHILDREN_DATA); + if (isDefined(value)) { + animateChildren = value; + } + } + + // there is no need to continue traversing at this point + if (parentAnimationDetected && animateChildren === false) break; + + if (!bodyElementDetected) { + // we also need to ensure that the element is or will be a part of the body element + // otherwise it is pointless to even issue an animation to be rendered + bodyElementDetected = isMatchingElement(parentElement, bodyElement); + } + + if (bodyElementDetected && rootElementDetected) { + // If both body and root have been found, any other checks are pointless, + // as no animation data should live outside the application + break; + } + + if (!rootElementDetected) { + // If no rootElement is detected, check if the parentElement is pinned to another element + parentHost = jqLite.data(parentElement, NG_ANIMATE_PIN_DATA); + if (parentHost) { + // The pin target element becomes the next parent element + parentElement = getDomNode(parentHost); + continue; + } + } + + parentElement = parentElement.parentNode; + } + + var allowAnimation = (!parentAnimationDetected || animateChildren) && elementDisabled !== true; + return allowAnimation && rootElementDetected && bodyElementDetected; + } + + function markElementAnimationState(element, state, details) { + details = details || {}; + details.state = state; + + var node = getDomNode(element); + node.setAttribute(NG_ANIMATE_ATTR_NAME, state); + + var oldValue = activeAnimationsLookup.get(node); + var newValue = oldValue + ? extend(oldValue, details) + : details; + activeAnimationsLookup.put(node, newValue); + } + }]; +}]; + +var $$AnimationProvider = ['$animateProvider', function($animateProvider) { + var NG_ANIMATE_REF_ATTR = 'ng-animate-ref'; + + var drivers = this.drivers = []; + + var RUNNER_STORAGE_KEY = '$$animationRunner'; + + function setRunner(element, runner) { + element.data(RUNNER_STORAGE_KEY, runner); + } + + function removeRunner(element) { + element.removeData(RUNNER_STORAGE_KEY); + } + + function getRunner(element) { + return element.data(RUNNER_STORAGE_KEY); + } + + this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$HashMap', '$$rAFScheduler', + function($$jqLite, $rootScope, $injector, $$AnimateRunner, $$HashMap, $$rAFScheduler) { + + var animationQueue = []; + var applyAnimationClasses = applyAnimationClassesFactory($$jqLite); + + function sortAnimations(animations) { + var tree = { children: [] }; + var i, lookup = new $$HashMap(); + + // this is done first beforehand so that the hashmap + // is filled with a list of the elements that will be animated + for (i = 0; i < animations.length; i++) { + var animation = animations[i]; + lookup.put(animation.domNode, animations[i] = { + domNode: animation.domNode, + fn: animation.fn, + children: [] + }); + } + + for (i = 0; i < animations.length; i++) { + processNode(animations[i]); + } + + return flatten(tree); + + function processNode(entry) { + if (entry.processed) return entry; + entry.processed = true; + + var elementNode = entry.domNode; + var parentNode = elementNode.parentNode; + lookup.put(elementNode, entry); + + var parentEntry; + while (parentNode) { + parentEntry = lookup.get(parentNode); + if (parentEntry) { + if (!parentEntry.processed) { + parentEntry = processNode(parentEntry); + } + break; + } + parentNode = parentNode.parentNode; + } + + (parentEntry || tree).children.push(entry); + return entry; + } + + function flatten(tree) { + var result = []; + var queue = []; + var i; + + for (i = 0; i < tree.children.length; i++) { + queue.push(tree.children[i]); + } + + var remainingLevelEntries = queue.length; + var nextLevelEntries = 0; + var row = []; + + for (i = 0; i < queue.length; i++) { + var entry = queue[i]; + if (remainingLevelEntries <= 0) { + remainingLevelEntries = nextLevelEntries; + nextLevelEntries = 0; + result.push(row); + row = []; + } + row.push(entry.fn); + entry.children.forEach(function(childEntry) { + nextLevelEntries++; + queue.push(childEntry); + }); + remainingLevelEntries--; + } + + if (row.length) { + result.push(row); + } + + return result; + } + } + + // TODO(matsko): document the signature in a better way + return function(element, event, options) { + options = prepareAnimationOptions(options); + var isStructural = ['enter', 'move', 'leave'].indexOf(event) >= 0; + + // there is no animation at the current moment, however + // these runner methods will get later updated with the + // methods leading into the driver's end/cancel methods + // for now they just stop the animation from starting + var runner = new $$AnimateRunner({ + end: function() { close(); }, + cancel: function() { close(true); } + }); + + if (!drivers.length) { + close(); + return runner; + } + + setRunner(element, runner); + + var classes = mergeClasses(element.attr('class'), mergeClasses(options.addClass, options.removeClass)); + var tempClasses = options.tempClasses; + if (tempClasses) { + classes += ' ' + tempClasses; + options.tempClasses = null; + } + + var prepareClassName; + if (isStructural) { + prepareClassName = 'ng-' + event + PREPARE_CLASS_SUFFIX; + $$jqLite.addClass(element, prepareClassName); + } + + animationQueue.push({ + // this data is used by the postDigest code and passed into + // the driver step function + element: element, + classes: classes, + event: event, + structural: isStructural, + options: options, + beforeStart: beforeStart, + close: close + }); + + element.on('$destroy', handleDestroyedElement); + + // we only want there to be one function called within the post digest + // block. This way we can group animations for all the animations that + // were apart of the same postDigest flush call. + if (animationQueue.length > 1) return runner; + + $rootScope.$$postDigest(function() { + var animations = []; + forEach(animationQueue, function(entry) { + // the element was destroyed early on which removed the runner + // form its storage. This means we can't animate this element + // at all and it already has been closed due to destruction. + if (getRunner(entry.element)) { + animations.push(entry); + } else { + entry.close(); + } + }); + + // now any future animations will be in another postDigest + animationQueue.length = 0; + + var groupedAnimations = groupAnimations(animations); + var toBeSortedAnimations = []; + + forEach(groupedAnimations, function(animationEntry) { + toBeSortedAnimations.push({ + domNode: getDomNode(animationEntry.from ? animationEntry.from.element : animationEntry.element), + fn: function triggerAnimationStart() { + // it's important that we apply the `ng-animate` CSS class and the + // temporary classes before we do any driver invoking since these + // CSS classes may be required for proper CSS detection. + animationEntry.beforeStart(); + + var startAnimationFn, closeFn = animationEntry.close; + + // in the event that the element was removed before the digest runs or + // during the RAF sequencing then we should not trigger the animation. + var targetElement = animationEntry.anchors + ? (animationEntry.from.element || animationEntry.to.element) + : animationEntry.element; + + if (getRunner(targetElement)) { + var operation = invokeFirstDriver(animationEntry); + if (operation) { + startAnimationFn = operation.start; + } + } + + if (!startAnimationFn) { + closeFn(); + } else { + var animationRunner = startAnimationFn(); + animationRunner.done(function(status) { + closeFn(!status); + }); + updateAnimationRunners(animationEntry, animationRunner); + } + } + }); + }); + + // we need to sort each of the animations in order of parent to child + // relationships. This ensures that the child classes are applied at the + // right time. + $$rAFScheduler(sortAnimations(toBeSortedAnimations)); + }); + + return runner; + + // TODO(matsko): change to reference nodes + function getAnchorNodes(node) { + var SELECTOR = '[' + NG_ANIMATE_REF_ATTR + ']'; + var items = node.hasAttribute(NG_ANIMATE_REF_ATTR) + ? [node] + : node.querySelectorAll(SELECTOR); + var anchors = []; + forEach(items, function(node) { + var attr = node.getAttribute(NG_ANIMATE_REF_ATTR); + if (attr && attr.length) { + anchors.push(node); + } + }); + return anchors; + } + + function groupAnimations(animations) { + var preparedAnimations = []; + var refLookup = {}; + forEach(animations, function(animation, index) { + var element = animation.element; + var node = getDomNode(element); + var event = animation.event; + var enterOrMove = ['enter', 'move'].indexOf(event) >= 0; + var anchorNodes = animation.structural ? getAnchorNodes(node) : []; + + if (anchorNodes.length) { + var direction = enterOrMove ? 'to' : 'from'; + + forEach(anchorNodes, function(anchor) { + var key = anchor.getAttribute(NG_ANIMATE_REF_ATTR); + refLookup[key] = refLookup[key] || {}; + refLookup[key][direction] = { + animationID: index, + element: jqLite(anchor) + }; + }); + } else { + preparedAnimations.push(animation); + } + }); + + var usedIndicesLookup = {}; + var anchorGroups = {}; + forEach(refLookup, function(operations, key) { + var from = operations.from; + var to = operations.to; + + if (!from || !to) { + // only one of these is set therefore we can't have an + // anchor animation since all three pieces are required + var index = from ? from.animationID : to.animationID; + var indexKey = index.toString(); + if (!usedIndicesLookup[indexKey]) { + usedIndicesLookup[indexKey] = true; + preparedAnimations.push(animations[index]); + } + return; + } + + var fromAnimation = animations[from.animationID]; + var toAnimation = animations[to.animationID]; + var lookupKey = from.animationID.toString(); + if (!anchorGroups[lookupKey]) { + var group = anchorGroups[lookupKey] = { + structural: true, + beforeStart: function() { + fromAnimation.beforeStart(); + toAnimation.beforeStart(); + }, + close: function() { + fromAnimation.close(); + toAnimation.close(); + }, + classes: cssClassesIntersection(fromAnimation.classes, toAnimation.classes), + from: fromAnimation, + to: toAnimation, + anchors: [] // TODO(matsko): change to reference nodes + }; + + // the anchor animations require that the from and to elements both have at least + // one shared CSS class which effectively marries the two elements together to use + // the same animation driver and to properly sequence the anchor animation. + if (group.classes.length) { + preparedAnimations.push(group); + } else { + preparedAnimations.push(fromAnimation); + preparedAnimations.push(toAnimation); + } + } + + anchorGroups[lookupKey].anchors.push({ + 'out': from.element, 'in': to.element + }); + }); + + return preparedAnimations; + } + + function cssClassesIntersection(a,b) { + a = a.split(' '); + b = b.split(' '); + var matches = []; + + for (var i = 0; i < a.length; i++) { + var aa = a[i]; + if (aa.substring(0,3) === 'ng-') continue; + + for (var j = 0; j < b.length; j++) { + if (aa === b[j]) { + matches.push(aa); + break; + } + } + } + + return matches.join(' '); + } + + function invokeFirstDriver(animationDetails) { + // we loop in reverse order since the more general drivers (like CSS and JS) + // may attempt more elements, but custom drivers are more particular + for (var i = drivers.length - 1; i >= 0; i--) { + var driverName = drivers[i]; + if (!$injector.has(driverName)) continue; // TODO(matsko): remove this check + + var factory = $injector.get(driverName); + var driver = factory(animationDetails); + if (driver) { + return driver; + } + } + } + + function beforeStart() { + element.addClass(NG_ANIMATE_CLASSNAME); + if (tempClasses) { + $$jqLite.addClass(element, tempClasses); + } + if (prepareClassName) { + $$jqLite.removeClass(element, prepareClassName); + prepareClassName = null; + } + } + + function updateAnimationRunners(animation, newRunner) { + if (animation.from && animation.to) { + update(animation.from.element); + update(animation.to.element); + } else { + update(animation.element); + } + + function update(element) { + getRunner(element).setHost(newRunner); + } + } + + function handleDestroyedElement() { + var runner = getRunner(element); + if (runner && (event !== 'leave' || !options.$$domOperationFired)) { + runner.end(); + } + } + + function close(rejected) { // jshint ignore:line + element.off('$destroy', handleDestroyedElement); + removeRunner(element); + + applyAnimationClasses(element, options); + applyAnimationStyles(element, options); + options.domOperation(); + + if (tempClasses) { + $$jqLite.removeClass(element, tempClasses); + } + + element.removeClass(NG_ANIMATE_CLASSNAME); + runner.complete(!rejected); + } + }; + }]; +}]; + +/** + * @ngdoc directive + * @name ngAnimateSwap + * @restrict A + * @scope + * + * @description + * + * ngAnimateSwap is a animation-oriented directive that allows for the container to + * be removed and entered in whenever the associated expression changes. A + * common usecase for this directive is a rotating banner or slider component which + * contains one image being present at a time. When the active image changes + * then the old image will perform a `leave` animation and the new element + * will be inserted via an `enter` animation. + * + * @animations + * | Animation | Occurs | + * |----------------------------------|--------------------------------------| + * | {@link ng.$animate#enter enter} | when the new element is inserted to the DOM | + * | {@link ng.$animate#leave leave} | when the old element is removed from the DOM | + * + * @example + * + * + *
    + *
    + * {{ number }} + *
    + *
    + *
    + * + * angular.module('ngAnimateSwapExample', ['ngAnimate']) + * .controller('AppCtrl', ['$scope', '$interval', function($scope, $interval) { + * $scope.number = 0; + * $interval(function() { + * $scope.number++; + * }, 1000); + * + * var colors = ['red','blue','green','yellow','orange']; + * $scope.colorClass = function(number) { + * return colors[number % colors.length]; + * }; + * }]); + * + * + * .container { + * height:250px; + * width:250px; + * position:relative; + * overflow:hidden; + * border:2px solid black; + * } + * .container .cell { + * font-size:150px; + * text-align:center; + * line-height:250px; + * position:absolute; + * top:0; + * left:0; + * right:0; + * border-bottom:2px solid black; + * } + * .swap-animation.ng-enter, .swap-animation.ng-leave { + * transition:0.5s linear all; + * } + * .swap-animation.ng-enter { + * top:-250px; + * } + * .swap-animation.ng-enter-active { + * top:0px; + * } + * .swap-animation.ng-leave { + * top:0px; + * } + * .swap-animation.ng-leave-active { + * top:250px; + * } + * .red { background:red; } + * .green { background:green; } + * .blue { background:blue; } + * .yellow { background:yellow; } + * .orange { background:orange; } + * + *
    + */ +var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $rootScope) { + return { + restrict: 'A', + transclude: 'element', + terminal: true, + priority: 600, // we use 600 here to ensure that the directive is caught before others + link: function(scope, $element, attrs, ctrl, $transclude) { + var previousElement, previousScope; + scope.$watchCollection(attrs.ngAnimateSwap || attrs['for'], function(value) { + if (previousElement) { + $animate.leave(previousElement); + } + if (previousScope) { + previousScope.$destroy(); + previousScope = null; + } + if (value || value === 0) { + previousScope = scope.$new(); + $transclude(previousScope, function(element) { + previousElement = element; + $animate.enter(element, null, $element); + }); + } + }); + } + }; +}]; + +/* global angularAnimateModule: true, + + ngAnimateSwapDirective, + $$AnimateAsyncRunFactory, + $$rAFSchedulerFactory, + $$AnimateChildrenDirective, + $$AnimateQueueProvider, + $$AnimationProvider, + $AnimateCssProvider, + $$AnimateCssDriverProvider, + $$AnimateJsProvider, + $$AnimateJsDriverProvider, +*/ + +/** + * @ngdoc module + * @name ngAnimate + * @description + * + * The `ngAnimate` module provides support for CSS-based animations (keyframes and transitions) as well as JavaScript-based animations via + * callback hooks. Animations are not enabled by default, however, by including `ngAnimate` the animation hooks are enabled for an Angular app. + * + *
    + * + * # Usage + * Simply put, there are two ways to make use of animations when ngAnimate is used: by using **CSS** and **JavaScript**. The former works purely based + * using CSS (by using matching CSS selectors/styles) and the latter triggers animations that are registered via `module.animation()`. For + * both CSS and JS animations the sole requirement is to have a matching `CSS class` that exists both in the registered animation and within + * the HTML element that the animation will be triggered on. + * + * ## Directive Support + * The following directives are "animation aware": + * + * | Directive | Supported Animations | + * |----------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------| + * | {@link ng.directive:ngRepeat#animations ngRepeat} | enter, leave and move | + * | {@link ngRoute.directive:ngView#animations ngView} | enter and leave | + * | {@link ng.directive:ngInclude#animations ngInclude} | enter and leave | + * | {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave | + * | {@link ng.directive:ngIf#animations ngIf} | enter and leave | + * | {@link ng.directive:ngClass#animations ngClass} | add and remove (the CSS class(es) present) | + * | {@link ng.directive:ngShow#animations ngShow} & {@link ng.directive:ngHide#animations ngHide} | add and remove (the ng-hide class value) | + * | {@link ng.directive:form#animation-hooks form} & {@link ng.directive:ngModel#animation-hooks ngModel} | add and remove (dirty, pristine, valid, invalid & all other validations) | + * | {@link module:ngMessages#animations ngMessages} | add and remove (ng-active & ng-inactive) | + * | {@link module:ngMessages#animations ngMessage} | enter and leave | + * + * (More information can be found by visiting each the documentation associated with each directive.) + * + * ## CSS-based Animations + * + * CSS-based animations with ngAnimate are unique since they require no JavaScript code at all. By using a CSS class that we reference between our HTML + * and CSS code we can create an animation that will be picked up by Angular when an the underlying directive performs an operation. + * + * The example below shows how an `enter` animation can be made possible on an element using `ng-if`: + * + * ```html + *
    + * Fade me in out + *
    + * + * + * ``` + * + * Notice the CSS class **fade**? We can now create the CSS transition code that references this class: + * + * ```css + * /* The starting CSS styles for the enter animation */ + * .fade.ng-enter { + * transition:0.5s linear all; + * opacity:0; + * } + * + * /* The finishing CSS styles for the enter animation */ + * .fade.ng-enter.ng-enter-active { + * opacity:1; + * } + * ``` + * + * The key thing to remember here is that, depending on the animation event (which each of the directives above trigger depending on what's going on) two + * generated CSS classes will be applied to the element; in the example above we have `.ng-enter` and `.ng-enter-active`. For CSS transitions, the transition + * code **must** be defined within the starting CSS class (in this case `.ng-enter`). The destination class is what the transition will animate towards. + * + * If for example we wanted to create animations for `leave` and `move` (ngRepeat triggers move) then we can do so using the same CSS naming conventions: + * + * ```css + * /* now the element will fade out before it is removed from the DOM */ + * .fade.ng-leave { + * transition:0.5s linear all; + * opacity:1; + * } + * .fade.ng-leave.ng-leave-active { + * opacity:0; + * } + * ``` + * + * We can also make use of **CSS Keyframes** by referencing the keyframe animation within the starting CSS class: + * + * ```css + * /* there is no need to define anything inside of the destination + * CSS class since the keyframe will take charge of the animation */ + * .fade.ng-leave { + * animation: my_fade_animation 0.5s linear; + * -webkit-animation: my_fade_animation 0.5s linear; + * } + * + * @keyframes my_fade_animation { + * from { opacity:1; } + * to { opacity:0; } + * } + * + * @-webkit-keyframes my_fade_animation { + * from { opacity:1; } + * to { opacity:0; } + * } + * ``` + * + * Feel free also mix transitions and keyframes together as well as any other CSS classes on the same element. + * + * ### CSS Class-based Animations + * + * Class-based animations (animations that are triggered via `ngClass`, `ngShow`, `ngHide` and some other directives) have a slightly different + * naming convention. Class-based animations are basic enough that a standard transition or keyframe can be referenced on the class being added + * and removed. + * + * For example if we wanted to do a CSS animation for `ngHide` then we place an animation on the `.ng-hide` CSS class: + * + * ```html + *
    + * Show and hide me + *
    + * + * + * + * ``` + * + * All that is going on here with ngShow/ngHide behind the scenes is the `.ng-hide` class is added/removed (when the hidden state is valid). Since + * ngShow and ngHide are animation aware then we can match up a transition and ngAnimate handles the rest. + * + * In addition the addition and removal of the CSS class, ngAnimate also provides two helper methods that we can use to further decorate the animation + * with CSS styles. + * + * ```html + *
    + * Highlight this box + *
    + * + * + * + * ``` + * + * We can also make use of CSS keyframes by placing them within the CSS classes. + * + * + * ### CSS Staggering Animations + * A Staggering animation is a collection of animations that are issued with a slight delay in between each successive operation resulting in a + * curtain-like effect. The ngAnimate module (versions >=1.2) supports staggering animations and the stagger effect can be + * performed by creating a **ng-EVENT-stagger** CSS class and attaching that class to the base CSS class used for + * the animation. The style property expected within the stagger class can either be a **transition-delay** or an + * **animation-delay** property (or both if your animation contains both transitions and keyframe animations). + * + * ```css + * .my-animation.ng-enter { + * /* standard transition code */ + * transition: 1s linear all; + * opacity:0; + * } + * .my-animation.ng-enter-stagger { + * /* this will have a 100ms delay between each successive leave animation */ + * transition-delay: 0.1s; + * + * /* As of 1.4.4, this must always be set: it signals ngAnimate + * to not accidentally inherit a delay property from another CSS class */ + * transition-duration: 0s; + * } + * .my-animation.ng-enter.ng-enter-active { + * /* standard transition styles */ + * opacity:1; + * } + * ``` + * + * Staggering animations work by default in ngRepeat (so long as the CSS class is defined). Outside of ngRepeat, to use staggering animations + * on your own, they can be triggered by firing multiple calls to the same event on $animate. However, the restrictions surrounding this + * are that each of the elements must have the same CSS className value as well as the same parent element. A stagger operation + * will also be reset if one or more animation frames have passed since the multiple calls to `$animate` were fired. + * + * The following code will issue the **ng-leave-stagger** event on the element provided: + * + * ```js + * var kids = parent.children(); + * + * $animate.leave(kids[0]); //stagger index=0 + * $animate.leave(kids[1]); //stagger index=1 + * $animate.leave(kids[2]); //stagger index=2 + * $animate.leave(kids[3]); //stagger index=3 + * $animate.leave(kids[4]); //stagger index=4 + * + * window.requestAnimationFrame(function() { + * //stagger has reset itself + * $animate.leave(kids[5]); //stagger index=0 + * $animate.leave(kids[6]); //stagger index=1 + * + * $scope.$digest(); + * }); + * ``` + * + * Stagger animations are currently only supported within CSS-defined animations. + * + * ### The `ng-animate` CSS class + * + * When ngAnimate is animating an element it will apply the `ng-animate` CSS class to the element for the duration of the animation. + * This is a temporary CSS class and it will be removed once the animation is over (for both JavaScript and CSS-based animations). + * + * Therefore, animations can be applied to an element using this temporary class directly via CSS. + * + * ```css + * .zipper.ng-animate { + * transition:0.5s linear all; + * } + * .zipper.ng-enter { + * opacity:0; + * } + * .zipper.ng-enter.ng-enter-active { + * opacity:1; + * } + * .zipper.ng-leave { + * opacity:1; + * } + * .zipper.ng-leave.ng-leave-active { + * opacity:0; + * } + * ``` + * + * (Note that the `ng-animate` CSS class is reserved and it cannot be applied on an element directly since ngAnimate will always remove + * the CSS class once an animation has completed.) + * + * + * ### The `ng-[event]-prepare` class + * + * This is a special class that can be used to prevent unwanted flickering / flash of content before + * the actual animation starts. The class is added as soon as an animation is initialized, but removed + * before the actual animation starts (after waiting for a $digest). + * It is also only added for *structural* animations (`enter`, `move`, and `leave`). + * + * In practice, flickering can appear when nesting elements with structural animations such as `ngIf` + * into elements that have class-based animations such as `ngClass`. + * + * ```html + *
    + *
    + *
    + *
    + *
    + * ``` + * + * It is possible that during the `enter` animation, the `.message` div will be briefly visible before it starts animating. + * In that case, you can add styles to the CSS that make sure the element stays hidden before the animation starts: + * + * ```css + * .message.ng-enter-prepare { + * opacity: 0; + * } + * + * ``` + * + * ## JavaScript-based Animations + * + * ngAnimate also allows for animations to be consumed by JavaScript code. The approach is similar to CSS-based animations (where there is a shared + * CSS class that is referenced in our HTML code) but in addition we need to register the JavaScript animation on the module. By making use of the + * `module.animation()` module function we can register the animation. + * + * Let's see an example of a enter/leave animation using `ngRepeat`: + * + * ```html + *
    + * {{ item }} + *
    + * ``` + * + * See the **slide** CSS class? Let's use that class to define an animation that we'll structure in our module code by using `module.animation`: + * + * ```js + * myModule.animation('.slide', [function() { + * return { + * // make note that other events (like addClass/removeClass) + * // have different function input parameters + * enter: function(element, doneFn) { + * jQuery(element).fadeIn(1000, doneFn); + * + * // remember to call doneFn so that angular + * // knows that the animation has concluded + * }, + * + * move: function(element, doneFn) { + * jQuery(element).fadeIn(1000, doneFn); + * }, + * + * leave: function(element, doneFn) { + * jQuery(element).fadeOut(1000, doneFn); + * } + * } + * }]); + * ``` + * + * The nice thing about JS-based animations is that we can inject other services and make use of advanced animation libraries such as + * greensock.js and velocity.js. + * + * If our animation code class-based (meaning that something like `ngClass`, `ngHide` and `ngShow` triggers it) then we can still define + * our animations inside of the same registered animation, however, the function input arguments are a bit different: + * + * ```html + *
    + * this box is moody + *
    + * + * + * + * ``` + * + * ```js + * myModule.animation('.colorful', [function() { + * return { + * addClass: function(element, className, doneFn) { + * // do some cool animation and call the doneFn + * }, + * removeClass: function(element, className, doneFn) { + * // do some cool animation and call the doneFn + * }, + * setClass: function(element, addedClass, removedClass, doneFn) { + * // do some cool animation and call the doneFn + * } + * } + * }]); + * ``` + * + * ## CSS + JS Animations Together + * + * AngularJS 1.4 and higher has taken steps to make the amalgamation of CSS and JS animations more flexible. However, unlike earlier versions of Angular, + * defining CSS and JS animations to work off of the same CSS class will not work anymore. Therefore the example below will only result in **JS animations taking + * charge of the animation**: + * + * ```html + *
    + * Slide in and out + *
    + * ``` + * + * ```js + * myModule.animation('.slide', [function() { + * return { + * enter: function(element, doneFn) { + * jQuery(element).slideIn(1000, doneFn); + * } + * } + * }]); + * ``` + * + * ```css + * .slide.ng-enter { + * transition:0.5s linear all; + * transform:translateY(-100px); + * } + * .slide.ng-enter.ng-enter-active { + * transform:translateY(0); + * } + * ``` + * + * Does this mean that CSS and JS animations cannot be used together? Do JS-based animations always have higher priority? We can make up for the + * lack of CSS animations by using the `$animateCss` service to trigger our own tweaked-out, CSS-based animations directly from + * our own JS-based animation code: + * + * ```js + * myModule.animation('.slide', ['$animateCss', function($animateCss) { + * return { + * enter: function(element) { +* // this will trigger `.slide.ng-enter` and `.slide.ng-enter-active`. + * return $animateCss(element, { + * event: 'enter', + * structural: true + * }); + * } + * } + * }]); + * ``` + * + * The nice thing here is that we can save bandwidth by sticking to our CSS-based animation code and we don't need to rely on a 3rd-party animation framework. + * + * The `$animateCss` service is very powerful since we can feed in all kinds of extra properties that will be evaluated and fed into a CSS transition or + * keyframe animation. For example if we wanted to animate the height of an element while adding and removing classes then we can do so by providing that + * data into `$animateCss` directly: + * + * ```js + * myModule.animation('.slide', ['$animateCss', function($animateCss) { + * return { + * enter: function(element) { + * return $animateCss(element, { + * event: 'enter', + * structural: true, + * addClass: 'maroon-setting', + * from: { height:0 }, + * to: { height: 200 } + * }); + * } + * } + * }]); + * ``` + * + * Now we can fill in the rest via our transition CSS code: + * + * ```css + * /* the transition tells ngAnimate to make the animation happen */ + * .slide.ng-enter { transition:0.5s linear all; } + * + * /* this extra CSS class will be absorbed into the transition + * since the $animateCss code is adding the class */ + * .maroon-setting { background:red; } + * ``` + * + * And `$animateCss` will figure out the rest. Just make sure to have the `done()` callback fire the `doneFn` function to signal when the animation is over. + * + * To learn more about what's possible be sure to visit the {@link ngAnimate.$animateCss $animateCss service}. + * + * ## Animation Anchoring (via `ng-animate-ref`) + * + * ngAnimate in AngularJS 1.4 comes packed with the ability to cross-animate elements between + * structural areas of an application (like views) by pairing up elements using an attribute + * called `ng-animate-ref`. + * + * Let's say for example we have two views that are managed by `ng-view` and we want to show + * that there is a relationship between two components situated in within these views. By using the + * `ng-animate-ref` attribute we can identify that the two components are paired together and we + * can then attach an animation, which is triggered when the view changes. + * + * Say for example we have the following template code: + * + * ```html + * + *
    + *
    + * + * + * + * + * + * + * + * + * ``` + * + * Now, when the view changes (once the link is clicked), ngAnimate will examine the + * HTML contents to see if there is a match reference between any components in the view + * that is leaving and the view that is entering. It will scan both the view which is being + * removed (leave) and inserted (enter) to see if there are any paired DOM elements that + * contain a matching ref value. + * + * The two images match since they share the same ref value. ngAnimate will now create a + * transport element (which is a clone of the first image element) and it will then attempt + * to animate to the position of the second image element in the next view. For the animation to + * work a special CSS class called `ng-anchor` will be added to the transported element. + * + * We can now attach a transition onto the `.banner.ng-anchor` CSS class and then + * ngAnimate will handle the entire transition for us as well as the addition and removal of + * any changes of CSS classes between the elements: + * + * ```css + * .banner.ng-anchor { + * /* this animation will last for 1 second since there are + * two phases to the animation (an `in` and an `out` phase) */ + * transition:0.5s linear all; + * } + * ``` + * + * We also **must** include animations for the views that are being entered and removed + * (otherwise anchoring wouldn't be possible since the new view would be inserted right away). + * + * ```css + * .view-animation.ng-enter, .view-animation.ng-leave { + * transition:0.5s linear all; + * position:fixed; + * left:0; + * top:0; + * width:100%; + * } + * .view-animation.ng-enter { + * transform:translateX(100%); + * } + * .view-animation.ng-leave, + * .view-animation.ng-enter.ng-enter-active { + * transform:translateX(0%); + * } + * .view-animation.ng-leave.ng-leave-active { + * transform:translateX(-100%); + * } + * ``` + * + * Now we can jump back to the anchor animation. When the animation happens, there are two stages that occur: + * an `out` and an `in` stage. The `out` stage happens first and that is when the element is animated away + * from its origin. Once that animation is over then the `in` stage occurs which animates the + * element to its destination. The reason why there are two animations is to give enough time + * for the enter animation on the new element to be ready. + * + * The example above sets up a transition for both the in and out phases, but we can also target the out or + * in phases directly via `ng-anchor-out` and `ng-anchor-in`. + * + * ```css + * .banner.ng-anchor-out { + * transition: 0.5s linear all; + * + * /* the scale will be applied during the out animation, + * but will be animated away when the in animation runs */ + * transform: scale(1.2); + * } + * + * .banner.ng-anchor-in { + * transition: 1s linear all; + * } + * ``` + * + * + * + * + * ### Anchoring Demo + * + + + Home +
    +
    +
    +
    +
    + + angular.module('anchoringExample', ['ngAnimate', 'ngRoute']) + .config(['$routeProvider', function($routeProvider) { + $routeProvider.when('/', { + templateUrl: 'home.html', + controller: 'HomeController as home' + }); + $routeProvider.when('/profile/:id', { + templateUrl: 'profile.html', + controller: 'ProfileController as profile' + }); + }]) + .run(['$rootScope', function($rootScope) { + $rootScope.records = [ + { id:1, title: "Miss Beulah Roob" }, + { id:2, title: "Trent Morissette" }, + { id:3, title: "Miss Ava Pouros" }, + { id:4, title: "Rod Pouros" }, + { id:5, title: "Abdul Rice" }, + { id:6, title: "Laurie Rutherford Sr." }, + { id:7, title: "Nakia McLaughlin" }, + { id:8, title: "Jordon Blanda DVM" }, + { id:9, title: "Rhoda Hand" }, + { id:10, title: "Alexandrea Sauer" } + ]; + }]) + .controller('HomeController', [function() { + //empty + }]) + .controller('ProfileController', ['$rootScope', '$routeParams', function($rootScope, $routeParams) { + var index = parseInt($routeParams.id, 10); + var record = $rootScope.records[index - 1]; + + this.title = record.title; + this.id = record.id; + }]); + + +

    Welcome to the home page

    +

    Please click on an element

    + + {{ record.title }} + +
    + +
    + {{ profile.title }} +
    +
    + + .record { + display:block; + font-size:20px; + } + .profile { + background:black; + color:white; + font-size:100px; + } + .view-container { + position:relative; + } + .view-container > .view.ng-animate { + position:absolute; + top:0; + left:0; + width:100%; + min-height:500px; + } + .view.ng-enter, .view.ng-leave, + .record.ng-anchor { + transition:0.5s linear all; + } + .view.ng-enter { + transform:translateX(100%); + } + .view.ng-enter.ng-enter-active, .view.ng-leave { + transform:translateX(0%); + } + .view.ng-leave.ng-leave-active { + transform:translateX(-100%); + } + .record.ng-anchor-out { + background:red; + } + +
    + * + * ### How is the element transported? + * + * When an anchor animation occurs, ngAnimate will clone the starting element and position it exactly where the starting + * element is located on screen via absolute positioning. The cloned element will be placed inside of the root element + * of the application (where ng-app was defined) and all of the CSS classes of the starting element will be applied. The + * element will then animate into the `out` and `in` animations and will eventually reach the coordinates and match + * the dimensions of the destination element. During the entire animation a CSS class of `.ng-animate-shim` will be applied + * to both the starting and destination elements in order to hide them from being visible (the CSS styling for the class + * is: `visibility:hidden`). Once the anchor reaches its destination then it will be removed and the destination element + * will become visible since the shim class will be removed. + * + * ### How is the morphing handled? + * + * CSS Anchoring relies on transitions and keyframes and the internal code is intelligent enough to figure out + * what CSS classes differ between the starting element and the destination element. These different CSS classes + * will be added/removed on the anchor element and a transition will be applied (the transition that is provided + * in the anchor class). Long story short, ngAnimate will figure out what classes to add and remove which will + * make the transition of the element as smooth and automatic as possible. Be sure to use simple CSS classes that + * do not rely on DOM nesting structure so that the anchor element appears the same as the starting element (since + * the cloned element is placed inside of root element which is likely close to the body element). + * + * Note that if the root element is on the `` element then the cloned node will be placed inside of body. + * + * + * ## Using $animate in your directive code + * + * So far we've explored how to feed in animations into an Angular application, but how do we trigger animations within our own directives in our application? + * By injecting the `$animate` service into our directive code, we can trigger structural and class-based hooks which can then be consumed by animations. Let's + * imagine we have a greeting box that shows and hides itself when the data changes + * + * ```html + * Hi there + * ``` + * + * ```js + * ngModule.directive('greetingBox', ['$animate', function($animate) { + * return function(scope, element, attrs) { + * attrs.$observe('active', function(value) { + * value ? $animate.addClass(element, 'on') : $animate.removeClass(element, 'on'); + * }); + * }); + * }]); + * ``` + * + * Now the `on` CSS class is added and removed on the greeting box component. Now if we add a CSS class on top of the greeting box element + * in our HTML code then we can trigger a CSS or JS animation to happen. + * + * ```css + * /* normally we would create a CSS class to reference on the element */ + * greeting-box.on { transition:0.5s linear all; background:green; color:white; } + * ``` + * + * The `$animate` service contains a variety of other methods like `enter`, `leave`, `animate` and `setClass`. To learn more about what's + * possible be sure to visit the {@link ng.$animate $animate service API page}. + * + * + * ## Callbacks and Promises + * + * When `$animate` is called it returns a promise that can be used to capture when the animation has ended. Therefore if we were to trigger + * an animation (within our directive code) then we can continue performing directive and scope related activities after the animation has + * ended by chaining onto the returned promise that animation method returns. + * + * ```js + * // somewhere within the depths of the directive + * $animate.enter(element, parent).then(function() { + * //the animation has completed + * }); + * ``` + * + * (Note that earlier versions of Angular prior to v1.4 required the promise code to be wrapped using `$scope.$apply(...)`. This is not the case + * anymore.) + * + * In addition to the animation promise, we can also make use of animation-related callbacks within our directives and controller code by registering + * an event listener using the `$animate` service. Let's say for example that an animation was triggered on our view + * routing controller to hook into that: + * + * ```js + * ngModule.controller('HomePageController', ['$animate', function($animate) { + * $animate.on('enter', ngViewElement, function(element) { + * // the animation for this route has completed + * }]); + * }]) + * ``` + * + * (Note that you will need to trigger a digest within the callback to get angular to notice any scope-related changes.) + */ + +/** + * @ngdoc service + * @name $animate + * @kind object + * + * @description + * The ngAnimate `$animate` service documentation is the same for the core `$animate` service. + * + * Click here {@link ng.$animate to learn more about animations with `$animate`}. + */ +angular.module('ngAnimate', []) + .directive('ngAnimateSwap', ngAnimateSwapDirective) + + .directive('ngAnimateChildren', $$AnimateChildrenDirective) + .factory('$$rAFScheduler', $$rAFSchedulerFactory) + + .provider('$$animateQueue', $$AnimateQueueProvider) + .provider('$$animation', $$AnimationProvider) + + .provider('$animateCss', $AnimateCssProvider) + .provider('$$animateCssDriver', $$AnimateCssDriverProvider) + + .provider('$$animateJs', $$AnimateJsProvider) + .provider('$$animateJsDriver', $$AnimateJsDriverProvider); + + +})(window, window.angular); diff --git a/app/www/lib/ionic/js/angular/angular-animate.min.js b/app/www/lib/ionic/js/angular/angular-animate.min.js new file mode 100644 index 0000000..57108b2 --- /dev/null +++ b/app/www/lib/ionic/js/angular/angular-animate.min.js @@ -0,0 +1,56 @@ +/* + AngularJS v1.5.3 + (c) 2010-2016 Google, Inc. http://angularjs.org + License: MIT +*/ +(function(E,w,Va){'use strict';function ya(a,b,c){if(!a)throw Ka("areq",b||"?",c||"required");return a}function za(a,b){if(!a&&!b)return"";if(!a)return b;if(!b)return a;ba(a)&&(a=a.join(" "));ba(b)&&(b=b.join(" "));return a+" "+b}function La(a){var b={};a&&(a.to||a.from)&&(b.to=a.to,b.from=a.from);return b}function V(a,b,c){var d="";a=ba(a)?a:a&&I(a)&&a.length?a.split(/\s+/):[];q(a,function(a,f){a&&0=a&&(a=h,h=0,b.push(e),e=[]);e.push(f.fn);f.children.forEach(function(a){h++;c.push(a)});a--}e.length&&b.push(e);return b}(c)}var l=[],w=Q(a);return function(s,x,v){function h(a){a=a.hasAttribute("ng-animate-ref")?[a]:a.querySelectorAll("[ng-animate-ref]");var b=[];q(a,function(a){var c=a.getAttribute("ng-animate-ref");c&&c.length&&b.push(a)});return b}function P(a){var b=[],c={};q(a,function(a,g){var d=A(a.element),k=0<=["enter","move"].indexOf(a.event),d=a.structural? +h(d):[];if(d.length){var e=k?"to":"from";q(d,function(a){var b=a.getAttribute("ng-animate-ref");c[b]=c[b]||{};c[b][e]={animationID:g,element:H(a)}})}else b.push(a)});var d={},k={};q(c,function(c,h){var e=c.from,f=c.to;if(e&&f){var m=a[e.animationID],C=a[f.animationID],n=e.animationID.toString();if(!k[n]){var D=k[n]={structural:!0,beforeStart:function(){m.beforeStart();C.beforeStart()},close:function(){m.close();C.close()},classes:y(m.classes,C.classes),from:m,to:C,anchors:[]};D.classes.length?b.push(D): +(b.push(m),b.push(C))}k[n].anchors.push({out:e.element,"in":f.element})}else e=e?e.animationID:f.animationID,f=e.toString(),d[f]||(d[f]=!0,b.push(a[e]))});return b}function y(a,b){a=a.split(" ");b=b.split(" ");for(var c=[],d=0;d=Q&&b>=L&&(H=!0,n())}function K(){function b(){if(!P){D(!1);q(y,function(a){m.style[a[0]]=a[1]});h(a,g);e.addClass(a,ga);if(p.recalculateTimingStyles){na=m.className+" "+fa;ia=w(m,na);B=v(m,na,ia);$=B.maxDelay;I=Math.max($,0);L=B.maxDuration;if(0===L){n();return}p.hasTransitions=0z.expectedEndTime)?r.cancel(z.timer):f.push(n)}K&&(l=r(c,l,!1),f[0]={timer:l,expectedEndTime:d},f.push(n),a.data("$$animateCss",f)); +if(ea.length)a.on(ea.join(" "),C);g.to&&(g.cleanupStyles&&Ga(G,m,Object.keys(g.to)),Ba(a,g))}}function c(){var b=a.data("$$animateCss");if(b){for(var d=1;d + * + * See {@link ngResource.$resource `$resource`} for usage. + */ + +/** + * @ngdoc service + * @name $resource + * @requires $http + * @requires ng.$log + * @requires $q + * @requires ng.$timeout + * + * @description + * A factory which creates a resource object that lets you interact with + * [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources. + * + * The returned resource object has action methods which provide high-level behaviors without + * the need to interact with the low level {@link ng.$http $http} service. + * + * Requires the {@link ngResource `ngResource`} module to be installed. + * + * By default, trailing slashes will be stripped from the calculated URLs, + * which can pose problems with server backends that do not expect that + * behavior. This can be disabled by configuring the `$resourceProvider` like + * this: + * + * ```js + app.config(['$resourceProvider', function($resourceProvider) { + // Don't strip trailing slashes from calculated URLs + $resourceProvider.defaults.stripTrailingSlashes = false; + }]); + * ``` + * + * @param {string} url A parameterized URL template with parameters prefixed by `:` as in + * `/user/:username`. If you are using a URL with a port number (e.g. + * `http://example.com:8080/api`), it will be respected. + * + * If you are using a url with a suffix, just add the suffix, like this: + * `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')` + * or even `$resource('http://example.com/resource/:resource_id.:format')` + * If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be + * collapsed down to a single `.`. If you need this sequence to appear and not collapse then you + * can escape it with `/\.`. + * + * @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in + * `actions` methods. If a parameter value is a function, it will be executed every time + * when a param value needs to be obtained for a request (unless the param was overridden). + * + * Each key value in the parameter object is first bound to url template if present and then any + * excess keys are appended to the url search query after the `?`. + * + * Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in + * URL `/path/greet?salutation=Hello`. + * + * If the parameter value is prefixed with `@` then the value for that parameter will be extracted + * from the corresponding property on the `data` object (provided when calling an action method). + * For example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of + * `someParam` will be `data.someProp`. + * + * @param {Object.=} actions Hash with declaration of custom actions that should extend + * the default set of resource actions. The declaration should be created in the format of {@link + * ng.$http#usage $http.config}: + * + * {action1: {method:?, params:?, isArray:?, headers:?, ...}, + * action2: {method:?, params:?, isArray:?, headers:?, ...}, + * ...} + * + * Where: + * + * - **`action`** – {string} – The name of action. This name becomes the name of the method on + * your resource object. + * - **`method`** – {string} – Case insensitive HTTP method (e.g. `GET`, `POST`, `PUT`, + * `DELETE`, `JSONP`, etc). + * - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of + * the parameter value is a function, it will be executed every time when a param value needs to + * be obtained for a request (unless the param was overridden). + * - **`url`** – {string} – action specific `url` override. The url templating is supported just + * like for the resource-level urls. + * - **`isArray`** – {boolean=} – If true then the returned object for this action is an array, + * see `returns` section. + * - **`transformRequest`** – + * `{function(data, headersGetter)|Array.}` – + * transform function or an array of such functions. The transform function takes the http + * request body and headers and returns its transformed (typically serialized) version. + * By default, transformRequest will contain one function that checks if the request data is + * an object and serializes to using `angular.toJson`. To prevent this behavior, set + * `transformRequest` to an empty array: `transformRequest: []` + * - **`transformResponse`** – + * `{function(data, headersGetter)|Array.}` – + * transform function or an array of such functions. The transform function takes the http + * response body and headers and returns its transformed (typically deserialized) version. + * By default, transformResponse will contain one function that checks if the response looks + * like a JSON string and deserializes it using `angular.fromJson`. To prevent this behavior, + * set `transformResponse` to an empty array: `transformResponse: []` + * - **`cache`** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the + * GET request, otherwise if a cache instance built with + * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for + * caching. + * - **`timeout`** – `{number}` – timeout in milliseconds.
    + * **Note:** In contrast to {@link ng.$http#usage $http.config}, {@link ng.$q promises} are + * **not** supported in $resource, because the same value would be used for multiple requests. + * If you are looking for a way to cancel requests, you should use the `cancellable` option. + * - **`cancellable`** – `{boolean}` – if set to true, the request made by a "non-instance" call + * will be cancelled (if not already completed) by calling `$cancelRequest()` on the call's + * return value. Calling `$cancelRequest()` for a non-cancellable or an already + * completed/cancelled request will have no effect.
    + * - **`withCredentials`** - `{boolean}` - whether to set the `withCredentials` flag on the + * XHR object. See + * [requests with credentials](https://developer.mozilla.org/en/http_access_control#section_5) + * for more information. + * - **`responseType`** - `{string}` - see + * [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType). + * - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods - + * `response` and `responseError`. Both `response` and `responseError` interceptors get called + * with `http response` object. See {@link ng.$http $http interceptors}. + * + * @param {Object} options Hash with custom settings that should extend the + * default `$resourceProvider` behavior. The supported options are: + * + * - **`stripTrailingSlashes`** – {boolean} – If true then the trailing + * slashes from any calculated URL will be stripped. (Defaults to true.) + * - **`cancellable`** – {boolean} – If true, the request made by a "non-instance" call will be + * cancelled (if not already completed) by calling `$cancelRequest()` on the call's return value. + * This can be overwritten per action. (Defaults to false.) + * + * @returns {Object} A resource "class" object with methods for the default set of resource actions + * optionally extended with custom `actions`. The default set contains these actions: + * ```js + * { 'get': {method:'GET'}, + * 'save': {method:'POST'}, + * 'query': {method:'GET', isArray:true}, + * 'remove': {method:'DELETE'}, + * 'delete': {method:'DELETE'} }; + * ``` + * + * Calling these methods invoke an {@link ng.$http} with the specified http method, + * destination and parameters. When the data is returned from the server then the object is an + * instance of the resource class. The actions `save`, `remove` and `delete` are available on it + * as methods with the `$` prefix. This allows you to easily perform CRUD operations (create, + * read, update, delete) on server-side data like this: + * ```js + * var User = $resource('/user/:userId', {userId:'@id'}); + * var user = User.get({userId:123}, function() { + * user.abc = true; + * user.$save(); + * }); + * ``` + * + * It is important to realize that invoking a $resource object method immediately returns an + * empty reference (object or array depending on `isArray`). Once the data is returned from the + * server the existing reference is populated with the actual data. This is a useful trick since + * usually the resource is assigned to a model which is then rendered by the view. Having an empty + * object results in no rendering, once the data arrives from the server then the object is + * populated with the data and the view automatically re-renders itself showing the new data. This + * means that in most cases one never has to write a callback function for the action methods. + * + * The action methods on the class object or instance object can be invoked with the following + * parameters: + * + * - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])` + * - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])` + * - non-GET instance actions: `instance.$action([parameters], [success], [error])` + * + * + * Success callback is called with (value, responseHeaders) arguments, where the value is + * the populated resource instance or collection object. The error callback is called + * with (httpResponse) argument. + * + * Class actions return empty instance (with additional properties below). + * Instance actions return promise of the action. + * + * The Resource instances and collections have these additional properties: + * + * - `$promise`: the {@link ng.$q promise} of the original server interaction that created this + * instance or collection. + * + * On success, the promise is resolved with the same resource instance or collection object, + * updated with data from server. This makes it easy to use in + * {@link ngRoute.$routeProvider resolve section of $routeProvider.when()} to defer view + * rendering until the resource(s) are loaded. + * + * On failure, the promise is rejected with the {@link ng.$http http response} object, without + * the `resource` property. + * + * If an interceptor object was provided, the promise will instead be resolved with the value + * returned by the interceptor. + * + * - `$resolved`: `true` after first server interaction is completed (either with success or + * rejection), `false` before that. Knowing if the Resource has been resolved is useful in + * data-binding. + * + * The Resource instances and collections have these additional methods: + * + * - `$cancelRequest`: If there is a cancellable, pending request related to the instance or + * collection, calling this method will abort the request. + * + * @example + * + * # Credit card resource + * + * ```js + // Define CreditCard class + var CreditCard = $resource('/user/:userId/card/:cardId', + {userId:123, cardId:'@id'}, { + charge: {method:'POST', params:{charge:true}} + }); + + // We can retrieve a collection from the server + var cards = CreditCard.query(function() { + // GET: /user/123/card + // server returns: [ {id:456, number:'1234', name:'Smith'} ]; + + var card = cards[0]; + // each item is an instance of CreditCard + expect(card instanceof CreditCard).toEqual(true); + card.name = "J. Smith"; + // non GET methods are mapped onto the instances + card.$save(); + // POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'} + // server returns: {id:456, number:'1234', name: 'J. Smith'}; + + // our custom method is mapped as well. + card.$charge({amount:9.99}); + // POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'} + }); + + // we can create an instance as well + var newCard = new CreditCard({number:'0123'}); + newCard.name = "Mike Smith"; + newCard.$save(); + // POST: /user/123/card {number:'0123', name:'Mike Smith'} + // server returns: {id:789, number:'0123', name: 'Mike Smith'}; + expect(newCard.id).toEqual(789); + * ``` + * + * The object returned from this function execution is a resource "class" which has "static" method + * for each action in the definition. + * + * Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and + * `headers`. + * + * @example + * + * # User resource + * + * When the data is returned from the server then the object is an instance of the resource type and + * all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD + * operations (create, read, update, delete) on server-side data. + + ```js + var User = $resource('/user/:userId', {userId:'@id'}); + User.get({userId:123}, function(user) { + user.abc = true; + user.$save(); + }); + ``` + * + * It's worth noting that the success callback for `get`, `query` and other methods gets passed + * in the response that came from the server as well as $http header getter function, so one + * could rewrite the above example and get access to http headers as: + * + ```js + var User = $resource('/user/:userId', {userId:'@id'}); + User.get({userId:123}, function(user, getResponseHeaders){ + user.abc = true; + user.$save(function(user, putResponseHeaders) { + //user => saved user object + //putResponseHeaders => $http header getter + }); + }); + ``` + * + * You can also access the raw `$http` promise via the `$promise` property on the object returned + * + ``` + var User = $resource('/user/:userId', {userId:'@id'}); + User.get({userId:123}) + .$promise.then(function(user) { + $scope.user = user; + }); + ``` + * + * @example + * + * # Creating a custom 'PUT' request + * + * In this example we create a custom method on our resource to make a PUT request + * ```js + * var app = angular.module('app', ['ngResource', 'ngRoute']); + * + * // Some APIs expect a PUT request in the format URL/object/ID + * // Here we are creating an 'update' method + * app.factory('Notes', ['$resource', function($resource) { + * return $resource('/notes/:id', null, + * { + * 'update': { method:'PUT' } + * }); + * }]); + * + * // In our controller we get the ID from the URL using ngRoute and $routeParams + * // We pass in $routeParams and our Notes factory along with $scope + * app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes', + function($scope, $routeParams, Notes) { + * // First get a note object from the factory + * var note = Notes.get({ id:$routeParams.id }); + * $id = note.id; + * + * // Now call update passing in the ID first then the object you are updating + * Notes.update({ id:$id }, note); + * + * // This will PUT /notes/ID with the note object in the request payload + * }]); + * ``` + * + * @example + * + * # Cancelling requests + * + * If an action's configuration specifies that it is cancellable, you can cancel the request related + * to an instance or collection (as long as it is a result of a "non-instance" call): + * + ```js + // ...defining the `Hotel` resource... + var Hotel = $resource('/api/hotel/:id', {id: '@id'}, { + // Let's make the `query()` method cancellable + query: {method: 'get', isArray: true, cancellable: true} + }); + + // ...somewhere in the PlanVacationController... + ... + this.onDestinationChanged = function onDestinationChanged(destination) { + // We don't care about any pending request for hotels + // in a different destination any more + this.availableHotels.$cancelRequest(); + + // Let's query for hotels in '' + // (calls: /api/hotel?location=) + this.availableHotels = Hotel.query({location: destination}); + }; + ``` + * + */ +angular.module('ngResource', ['ng']). + provider('$resource', function() { + var PROTOCOL_AND_DOMAIN_REGEX = /^https?:\/\/[^\/]*/; + var provider = this; + + this.defaults = { + // Strip slashes by default + stripTrailingSlashes: true, + + // Default actions configuration + actions: { + 'get': {method: 'GET'}, + 'save': {method: 'POST'}, + 'query': {method: 'GET', isArray: true}, + 'remove': {method: 'DELETE'}, + 'delete': {method: 'DELETE'} + } + }; + + this.$get = ['$http', '$log', '$q', '$timeout', function($http, $log, $q, $timeout) { + + var noop = angular.noop, + forEach = angular.forEach, + extend = angular.extend, + copy = angular.copy, + isFunction = angular.isFunction; + + /** + * We need our custom method because encodeURIComponent is too aggressive and doesn't follow + * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set + * (pchar) allowed in path segments: + * segment = *pchar + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + * pct-encoded = "%" HEXDIG HEXDIG + * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + * / "*" / "+" / "," / ";" / "=" + */ + function encodeUriSegment(val) { + return encodeUriQuery(val, true). + replace(/%26/gi, '&'). + replace(/%3D/gi, '='). + replace(/%2B/gi, '+'); + } + + + /** + * This method is intended for encoding *key* or *value* parts of query component. We need a + * custom method because encodeURIComponent is too aggressive and encodes stuff that doesn't + * have to be encoded per http://tools.ietf.org/html/rfc3986: + * query = *( pchar / "/" / "?" ) + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + * pct-encoded = "%" HEXDIG HEXDIG + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + * / "*" / "+" / "," / ";" / "=" + */ + function encodeUriQuery(val, pctEncodeSpaces) { + return encodeURIComponent(val). + replace(/%40/gi, '@'). + replace(/%3A/gi, ':'). + replace(/%24/g, '$'). + replace(/%2C/gi, ','). + replace(/%20/g, (pctEncodeSpaces ? '%20' : '+')); + } + + function Route(template, defaults) { + this.template = template; + this.defaults = extend({}, provider.defaults, defaults); + this.urlParams = {}; + } + + Route.prototype = { + setUrlParams: function(config, params, actionUrl) { + var self = this, + url = actionUrl || self.template, + val, + encodedVal, + protocolAndDomain = ''; + + var urlParams = self.urlParams = {}; + forEach(url.split(/\W/), function(param) { + if (param === 'hasOwnProperty') { + throw $resourceMinErr('badname', "hasOwnProperty is not a valid parameter name."); + } + if (!(new RegExp("^\\d+$").test(param)) && param && + (new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) { + urlParams[param] = { + isQueryParamValue: (new RegExp("\\?.*=:" + param + "(?:\\W|$)")).test(url) + }; + } + }); + url = url.replace(/\\:/g, ':'); + url = url.replace(PROTOCOL_AND_DOMAIN_REGEX, function(match) { + protocolAndDomain = match; + return ''; + }); + + params = params || {}; + forEach(self.urlParams, function(paramInfo, urlParam) { + val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam]; + if (angular.isDefined(val) && val !== null) { + if (paramInfo.isQueryParamValue) { + encodedVal = encodeUriQuery(val, true); + } else { + encodedVal = encodeUriSegment(val); + } + url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), function(match, p1) { + return encodedVal + p1; + }); + } else { + url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match, + leadingSlashes, tail) { + if (tail.charAt(0) == '/') { + return tail; + } else { + return leadingSlashes + tail; + } + }); + } + }); + + // strip trailing slashes and set the url (unless this behavior is specifically disabled) + if (self.defaults.stripTrailingSlashes) { + url = url.replace(/\/+$/, '') || '/'; + } + + // then replace collapse `/.` if found in the last URL path segment before the query + // E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x` + url = url.replace(/\/\.(?=\w+($|\?))/, '.'); + // replace escaped `/\.` with `/.` + config.url = protocolAndDomain + url.replace(/\/\\\./, '/.'); + + + // set params - delegate param encoding to $http + forEach(params, function(value, key) { + if (!self.urlParams[key]) { + config.params = config.params || {}; + config.params[key] = value; + } + }); + } + }; + + + function resourceFactory(url, paramDefaults, actions, options) { + var route = new Route(url, options); + + actions = extend({}, provider.defaults.actions, actions); + + function extractParams(data, actionParams) { + var ids = {}; + actionParams = extend({}, paramDefaults, actionParams); + forEach(actionParams, function(value, key) { + if (isFunction(value)) { value = value(); } + ids[key] = value && value.charAt && value.charAt(0) == '@' ? + lookupDottedPath(data, value.substr(1)) : value; + }); + return ids; + } + + function defaultResponseInterceptor(response) { + return response.resource; + } + + function Resource(value) { + shallowClearAndCopy(value || {}, this); + } + + Resource.prototype.toJSON = function() { + var data = extend({}, this); + delete data.$promise; + delete data.$resolved; + return data; + }; + + forEach(actions, function(action, name) { + var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method); + var numericTimeout = action.timeout; + var cancellable = angular.isDefined(action.cancellable) ? action.cancellable : + (options && angular.isDefined(options.cancellable)) ? options.cancellable : + provider.defaults.cancellable; + + if (numericTimeout && !angular.isNumber(numericTimeout)) { + $log.debug('ngResource:\n' + + ' Only numeric values are allowed as `timeout`.\n' + + ' Promises are not supported in $resource, because the same value would ' + + 'be used for multiple requests. If you are looking for a way to cancel ' + + 'requests, you should use the `cancellable` option.'); + delete action.timeout; + numericTimeout = null; + } + + Resource[name] = function(a1, a2, a3, a4) { + var params = {}, data, success, error; + + /* jshint -W086 */ /* (purposefully fall through case statements) */ + switch (arguments.length) { + case 4: + error = a4; + success = a3; + //fallthrough + case 3: + case 2: + if (isFunction(a2)) { + if (isFunction(a1)) { + success = a1; + error = a2; + break; + } + + success = a2; + error = a3; + //fallthrough + } else { + params = a1; + data = a2; + success = a3; + break; + } + case 1: + if (isFunction(a1)) success = a1; + else if (hasBody) data = a1; + else params = a1; + break; + case 0: break; + default: + throw $resourceMinErr('badargs', + "Expected up to 4 arguments [params, data, success, error], got {0} arguments", + arguments.length); + } + /* jshint +W086 */ /* (purposefully fall through case statements) */ + + var isInstanceCall = this instanceof Resource; + var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data)); + var httpConfig = {}; + var responseInterceptor = action.interceptor && action.interceptor.response || + defaultResponseInterceptor; + var responseErrorInterceptor = action.interceptor && action.interceptor.responseError || + undefined; + var timeoutDeferred; + var numericTimeoutPromise; + + forEach(action, function(value, key) { + switch (key) { + default: + httpConfig[key] = copy(value); + break; + case 'params': + case 'isArray': + case 'interceptor': + case 'cancellable': + break; + } + }); + + if (!isInstanceCall && cancellable) { + timeoutDeferred = $q.defer(); + httpConfig.timeout = timeoutDeferred.promise; + + if (numericTimeout) { + numericTimeoutPromise = $timeout(timeoutDeferred.resolve, numericTimeout); + } + } + + if (hasBody) httpConfig.data = data; + route.setUrlParams(httpConfig, + extend({}, extractParams(data, action.params || {}), params), + action.url); + + var promise = $http(httpConfig).then(function(response) { + var data = response.data; + + if (data) { + // Need to convert action.isArray to boolean in case it is undefined + // jshint -W018 + if (angular.isArray(data) !== (!!action.isArray)) { + throw $resourceMinErr('badcfg', + 'Error in resource configuration for action `{0}`. Expected response to ' + + 'contain an {1} but got an {2} (Request: {3} {4})', name, action.isArray ? 'array' : 'object', + angular.isArray(data) ? 'array' : 'object', httpConfig.method, httpConfig.url); + } + // jshint +W018 + if (action.isArray) { + value.length = 0; + forEach(data, function(item) { + if (typeof item === "object") { + value.push(new Resource(item)); + } else { + // Valid JSON values may be string literals, and these should not be converted + // into objects. These items will not have access to the Resource prototype + // methods, but unfortunately there + value.push(item); + } + }); + } else { + var promise = value.$promise; // Save the promise + shallowClearAndCopy(data, value); + value.$promise = promise; // Restore the promise + } + } + response.resource = value; + + return response; + }, function(response) { + (error || noop)(response); + return $q.reject(response); + }); + + promise['finally'](function() { + value.$resolved = true; + if (!isInstanceCall && cancellable) { + value.$cancelRequest = angular.noop; + $timeout.cancel(numericTimeoutPromise); + timeoutDeferred = numericTimeoutPromise = httpConfig.timeout = null; + } + }); + + promise = promise.then( + function(response) { + var value = responseInterceptor(response); + (success || noop)(value, response.headers); + return value; + }, + responseErrorInterceptor); + + if (!isInstanceCall) { + // we are creating instance / collection + // - set the initial promise + // - return the instance / collection + value.$promise = promise; + value.$resolved = false; + if (cancellable) value.$cancelRequest = timeoutDeferred.resolve; + + return value; + } + + // instance call + return promise; + }; + + + Resource.prototype['$' + name] = function(params, success, error) { + if (isFunction(params)) { + error = success; success = params; params = {}; + } + var result = Resource[name].call(this, params, this, success, error); + return result.$promise || result; + }; + }); + + Resource.bind = function(additionalParamDefaults) { + return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions); + }; + + return Resource; + } + + return resourceFactory; + }]; + }); + + +})(window, window.angular); diff --git a/app/www/lib/ionic/js/angular/angular-resource.min.js b/app/www/lib/ionic/js/angular/angular-resource.min.js new file mode 100644 index 0000000..0fbd12e --- /dev/null +++ b/app/www/lib/ionic/js/angular/angular-resource.min.js @@ -0,0 +1,15 @@ +/* + AngularJS v1.5.3 + (c) 2010-2016 Google, Inc. http://angularjs.org + License: MIT +*/ +(function(Q,d,G){'use strict';function H(t,g){g=g||{};d.forEach(g,function(d,q){delete g[q]});for(var q in t)!t.hasOwnProperty(q)||"$"===q.charAt(0)&&"$"===q.charAt(1)||(g[q]=t[q]);return g}var z=d.$$minErr("$resource"),N=/^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;d.module("ngResource",["ng"]).provider("$resource",function(){var t=/^https?:\/\/[^\/]*/,g=this;this.defaults={stripTrailingSlashes:!0,actions:{get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}}}; +this.$get=["$http","$log","$q","$timeout",function(q,M,I,J){function A(d,h){return encodeURIComponent(d).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,h?"%20":"+")}function B(d,h){this.template=d;this.defaults=v({},g.defaults,h);this.urlParams={}}function K(e,h,n,k){function c(a,b){var c={};b=v({},h,b);u(b,function(b,h){x(b)&&(b=b());var f;if(b&&b.charAt&&"@"==b.charAt(0)){f=a;var l=b.substr(1);if(null==l||""===l||"hasOwnProperty"===l||!N.test("."+ +l))throw z("badmember",l);for(var l=l.split("."),m=0,k=l.length;m + * + * See {@link ngSanitize.$sanitize `$sanitize`} for usage. + */ + +/** + * @ngdoc service + * @name $sanitize + * @kind function + * + * @description + * Sanitizes an html string by stripping all potentially dangerous tokens. + * + * The input is sanitized by parsing the HTML into tokens. All safe tokens (from a whitelist) are + * then serialized back to properly escaped html string. This means that no unsafe input can make + * it into the returned string. + * + * The whitelist for URL sanitization of attribute values is configured using the functions + * `aHrefSanitizationWhitelist` and `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider + * `$compileProvider`}. + * + * The input may also contain SVG markup if this is enabled via {@link $sanitizeProvider}. + * + * @param {string} html HTML input. + * @returns {string} Sanitized HTML. + * + * @example + + + +
    + Snippet: + + + + + + + + + + + + + + + + + + + + + + + + + +
    DirectiveHowSourceRendered
    ng-bind-htmlAutomatically uses $sanitize
    <div ng-bind-html="snippet">
    </div>
    ng-bind-htmlBypass $sanitize by explicitly trusting the dangerous value +
    <div ng-bind-html="deliberatelyTrustDangerousSnippet()">
    +</div>
    +
    ng-bindAutomatically escapes
    <div ng-bind="snippet">
    </div>
    +
    +
    + + it('should sanitize the html snippet by default', function() { + expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()). + toBe('

    an html\nclick here\nsnippet

    '); + }); + + it('should inline raw snippet if bound to a trusted value', function() { + expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()). + toBe("

    an html\n" + + "click here\n" + + "snippet

    "); + }); + + it('should escape snippet without any filter', function() { + expect(element(by.css('#bind-default div')).getInnerHtml()). + toBe("<p style=\"color:blue\">an html\n" + + "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" + + "snippet</p>"); + }); + + it('should update', function() { + element(by.model('snippet')).clear(); + element(by.model('snippet')).sendKeys('new text'); + expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()). + toBe('new text'); + expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).toBe( + 'new text'); + expect(element(by.css('#bind-default div')).getInnerHtml()).toBe( + "new <b onclick=\"alert(1)\">text</b>"); + }); +
    +
    + */ + + +/** + * @ngdoc provider + * @name $sanitizeProvider + * + * @description + * Creates and configures {@link $sanitize} instance. + */ +function $SanitizeProvider() { + var svgEnabled = false; + + this.$get = ['$$sanitizeUri', function($$sanitizeUri) { + if (svgEnabled) { + angular.extend(validElements, svgElements); + } + return function(html) { + var buf = []; + htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) { + return !/^unsafe:/.test($$sanitizeUri(uri, isImage)); + })); + return buf.join(''); + }; + }]; + + + /** + * @ngdoc method + * @name $sanitizeProvider#enableSvg + * @kind function + * + * @description + * Enables a subset of svg to be supported by the sanitizer. + * + *
    + *

    By enabling this setting without taking other precautions, you might expose your + * application to click-hijacking attacks. In these attacks, sanitized svg elements could be positioned + * outside of the containing element and be rendered over other elements on the page (e.g. a login + * link). Such behavior can then result in phishing incidents.

    + * + *

    To protect against these, explicitly setup `overflow: hidden` css rule for all potential svg + * tags within the sanitized content:

    + * + *
    + * + *
    
    +   *   .rootOfTheIncludedContent svg {
    +   *     overflow: hidden !important;
    +   *   }
    +   *   
    + *
    + * + * @param {boolean=} regexp New regexp to whitelist urls with. + * @returns {boolean|ng.$sanitizeProvider} Returns the currently configured value if called + * without an argument or self for chaining otherwise. + */ + this.enableSvg = function(enableSvg) { + if (angular.isDefined(enableSvg)) { + svgEnabled = enableSvg; + return this; + } else { + return svgEnabled; + } + }; +} + +function sanitizeText(chars) { + var buf = []; + var writer = htmlSanitizeWriter(buf, angular.noop); + writer.chars(chars); + return buf.join(''); +} + + +// Regular Expressions for parsing tags and attributes +var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g, + // Match everything outside of normal chars and " (quote character) + NON_ALPHANUMERIC_REGEXP = /([^\#-~ |!])/g; + + +// Good source of info about elements and attributes +// http://dev.w3.org/html5/spec/Overview.html#semantics +// http://simon.html5.org/html-elements + +// Safe Void Elements - HTML5 +// http://dev.w3.org/html5/spec/Overview.html#void-elements +var voidElements = toMap("area,br,col,hr,img,wbr"); + +// Elements that you can, intentionally, leave open (and which close themselves) +// http://dev.w3.org/html5/spec/Overview.html#optional-tags +var optionalEndTagBlockElements = toMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"), + optionalEndTagInlineElements = toMap("rp,rt"), + optionalEndTagElements = angular.extend({}, + optionalEndTagInlineElements, + optionalEndTagBlockElements); + +// Safe Block Elements - HTML5 +var blockElements = angular.extend({}, optionalEndTagBlockElements, toMap("address,article," + + "aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," + + "h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul")); + +// Inline Elements - HTML5 +var inlineElements = angular.extend({}, optionalEndTagInlineElements, toMap("a,abbr,acronym,b," + + "bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," + + "samp,small,span,strike,strong,sub,sup,time,tt,u,var")); + +// SVG Elements +// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements +// Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted. +// They can potentially allow for arbitrary javascript to be executed. See #11290 +var svgElements = toMap("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph," + + "hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline," + + "radialGradient,rect,stop,svg,switch,text,title,tspan"); + +// Blocked Elements (will be stripped) +var blockedElements = toMap("script,style"); + +var validElements = angular.extend({}, + voidElements, + blockElements, + inlineElements, + optionalEndTagElements); + +//Attributes that have href and hence need to be sanitized +var uriAttrs = toMap("background,cite,href,longdesc,src,xlink:href"); + +var htmlAttrs = toMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' + + 'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' + + 'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' + + 'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' + + 'valign,value,vspace,width'); + +// SVG attributes (without "id" and "name" attributes) +// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes +var svgAttrs = toMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' + + 'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' + + 'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' + + 'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' + + 'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' + + 'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' + + 'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' + + 'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' + + 'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' + + 'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' + + 'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' + + 'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' + + 'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' + + 'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' + + 'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true); + +var validAttrs = angular.extend({}, + uriAttrs, + svgAttrs, + htmlAttrs); + +function toMap(str, lowercaseKeys) { + var obj = {}, items = str.split(','), i; + for (i = 0; i < items.length; i++) { + obj[lowercaseKeys ? angular.lowercase(items[i]) : items[i]] = true; + } + return obj; +} + +var inertBodyElement; +(function(window) { + var doc; + if (window.document && window.document.implementation) { + doc = window.document.implementation.createHTMLDocument("inert"); + } else { + throw $sanitizeMinErr('noinert', "Can't create an inert html document"); + } + var docElement = doc.documentElement || doc.getDocumentElement(); + var bodyElements = docElement.getElementsByTagName('body'); + + // usually there should be only one body element in the document, but IE doesn't have any, so we need to create one + if (bodyElements.length === 1) { + inertBodyElement = bodyElements[0]; + } else { + var html = doc.createElement('html'); + inertBodyElement = doc.createElement('body'); + html.appendChild(inertBodyElement); + doc.appendChild(html); + } +})(window); + +/** + * @example + * htmlParser(htmlString, { + * start: function(tag, attrs) {}, + * end: function(tag) {}, + * chars: function(text) {}, + * comment: function(text) {} + * }); + * + * @param {string} html string + * @param {object} handler + */ +function htmlParser(html, handler) { + if (html === null || html === undefined) { + html = ''; + } else if (typeof html !== 'string') { + html = '' + html; + } + inertBodyElement.innerHTML = html; + + //mXSS protection + var mXSSAttempts = 5; + do { + if (mXSSAttempts === 0) { + throw $sanitizeMinErr('uinput', "Failed to sanitize html because the input is unstable"); + } + mXSSAttempts--; + + // strip custom-namespaced attributes on IE<=11 + if (document.documentMode <= 11) { + stripCustomNsAttrs(inertBodyElement); + } + html = inertBodyElement.innerHTML; //trigger mXSS + inertBodyElement.innerHTML = html; + } while (html !== inertBodyElement.innerHTML); + + var node = inertBodyElement.firstChild; + while (node) { + switch (node.nodeType) { + case 1: // ELEMENT_NODE + handler.start(node.nodeName.toLowerCase(), attrToMap(node.attributes)); + break; + case 3: // TEXT NODE + handler.chars(node.textContent); + break; + } + + var nextNode; + if (!(nextNode = node.firstChild)) { + if (node.nodeType == 1) { + handler.end(node.nodeName.toLowerCase()); + } + nextNode = node.nextSibling; + if (!nextNode) { + while (nextNode == null) { + node = node.parentNode; + if (node === inertBodyElement) break; + nextNode = node.nextSibling; + if (node.nodeType == 1) { + handler.end(node.nodeName.toLowerCase()); + } + } + } + } + node = nextNode; + } + + while (node = inertBodyElement.firstChild) { + inertBodyElement.removeChild(node); + } +} + +function attrToMap(attrs) { + var map = {}; + for (var i = 0, ii = attrs.length; i < ii; i++) { + var attr = attrs[i]; + map[attr.name] = attr.value; + } + return map; +} + + +/** + * Escapes all potentially dangerous characters, so that the + * resulting string can be safely inserted into attribute or + * element text. + * @param value + * @returns {string} escaped text + */ +function encodeEntities(value) { + return value. + replace(/&/g, '&'). + replace(SURROGATE_PAIR_REGEXP, function(value) { + var hi = value.charCodeAt(0); + var low = value.charCodeAt(1); + return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';'; + }). + replace(NON_ALPHANUMERIC_REGEXP, function(value) { + return '&#' + value.charCodeAt(0) + ';'; + }). + replace(//g, '>'); +} + +/** + * create an HTML/XML writer which writes to buffer + * @param {Array} buf use buf.join('') to get out sanitized html string + * @returns {object} in the form of { + * start: function(tag, attrs) {}, + * end: function(tag) {}, + * chars: function(text) {}, + * comment: function(text) {} + * } + */ +function htmlSanitizeWriter(buf, uriValidator) { + var ignoreCurrentElement = false; + var out = angular.bind(buf, buf.push); + return { + start: function(tag, attrs) { + tag = angular.lowercase(tag); + if (!ignoreCurrentElement && blockedElements[tag]) { + ignoreCurrentElement = tag; + } + if (!ignoreCurrentElement && validElements[tag] === true) { + out('<'); + out(tag); + angular.forEach(attrs, function(value, key) { + var lkey=angular.lowercase(key); + var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background'); + if (validAttrs[lkey] === true && + (uriAttrs[lkey] !== true || uriValidator(value, isImage))) { + out(' '); + out(key); + out('="'); + out(encodeEntities(value)); + out('"'); + } + }); + out('>'); + } + }, + end: function(tag) { + tag = angular.lowercase(tag); + if (!ignoreCurrentElement && validElements[tag] === true && voidElements[tag] !== true) { + out(''); + } + if (tag == ignoreCurrentElement) { + ignoreCurrentElement = false; + } + }, + chars: function(chars) { + if (!ignoreCurrentElement) { + out(encodeEntities(chars)); + } + } + }; +} + + +/** + * When IE9-11 comes across an unknown namespaced attribute e.g. 'xlink:foo' it adds 'xmlns:ns1' attribute to declare + * ns1 namespace and prefixes the attribute with 'ns1' (e.g. 'ns1:xlink:foo'). This is undesirable since we don't want + * to allow any of these custom attributes. This method strips them all. + * + * @param node Root element to process + */ +function stripCustomNsAttrs(node) { + if (node.nodeType === Node.ELEMENT_NODE) { + var attrs = node.attributes; + for (var i = 0, l = attrs.length; i < l; i++) { + var attrNode = attrs[i]; + var attrName = attrNode.name.toLowerCase(); + if (attrName === 'xmlns:ns1' || attrName.indexOf('ns1:') === 0) { + node.removeAttributeNode(attrNode); + i--; + l--; + } + } + } + + var nextNode = node.firstChild; + if (nextNode) { + stripCustomNsAttrs(nextNode); + } + + nextNode = node.nextSibling; + if (nextNode) { + stripCustomNsAttrs(nextNode); + } +} + + + +// define ngSanitize module and register $sanitize service +angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider); + +/* global sanitizeText: false */ + +/** + * @ngdoc filter + * @name linky + * @kind function + * + * @description + * Finds links in text input and turns them into html links. Supports `http/https/ftp/mailto` and + * plain email address links. + * + * Requires the {@link ngSanitize `ngSanitize`} module to be installed. + * + * @param {string} text Input text. + * @param {string} target Window (`_blank|_self|_parent|_top`) or named frame to open links in. + * @param {object|function(url)} [attributes] Add custom attributes to the link element. + * + * Can be one of: + * + * - `object`: A map of attributes + * - `function`: Takes the url as a parameter and returns a map of attributes + * + * If the map of attributes contains a value for `target`, it overrides the value of + * the target parameter. + * + * + * @returns {string} Html-linkified and {@link $sanitize sanitized} text. + * + * @usage + + * + * @example + + +
    + Snippet: + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FilterSourceRendered
    linky filter +
    <div ng-bind-html="snippet | linky">
    </div>
    +
    +
    +
    linky target +
    <div ng-bind-html="snippetWithSingleURL | linky:'_blank'">
    </div>
    +
    +
    +
    linky custom attributes +
    <div ng-bind-html="snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}">
    </div>
    +
    +
    +
    no filter
    <div ng-bind="snippet">
    </div>
    + + + angular.module('linkyExample', ['ngSanitize']) + .controller('ExampleController', ['$scope', function($scope) { + $scope.snippet = + 'Pretty text with some links:\n'+ + 'http://angularjs.org/,\n'+ + 'mailto:us@somewhere.org,\n'+ + 'another@somewhere.org,\n'+ + 'and one more: ftp://127.0.0.1/.'; + $scope.snippetWithSingleURL = 'http://angularjs.org/'; + }]); + + + it('should linkify the snippet with urls', function() { + expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()). + toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' + + 'another@somewhere.org, and one more: ftp://127.0.0.1/.'); + expect(element.all(by.css('#linky-filter a')).count()).toEqual(4); + }); + + it('should not linkify snippet without the linky filter', function() { + expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()). + toBe('Pretty text with some links: http://angularjs.org/, mailto:us@somewhere.org, ' + + 'another@somewhere.org, and one more: ftp://127.0.0.1/.'); + expect(element.all(by.css('#escaped-html a')).count()).toEqual(0); + }); + + it('should update', function() { + element(by.model('snippet')).clear(); + element(by.model('snippet')).sendKeys('new http://link.'); + expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()). + toBe('new http://link.'); + expect(element.all(by.css('#linky-filter a')).count()).toEqual(1); + expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()) + .toBe('new http://link.'); + }); + + it('should work with the target property', function() { + expect(element(by.id('linky-target')). + element(by.binding("snippetWithSingleURL | linky:'_blank'")).getText()). + toBe('http://angularjs.org/'); + expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank'); + }); + + it('should optionally add custom attributes', function() { + expect(element(by.id('linky-custom-attributes')). + element(by.binding("snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}")).getText()). + toBe('http://angularjs.org/'); + expect(element(by.css('#linky-custom-attributes a')).getAttribute('rel')).toEqual('nofollow'); + }); + + + */ +angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) { + var LINKY_URL_REGEXP = + /((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i, + MAILTO_REGEXP = /^mailto:/i; + + var linkyMinErr = angular.$$minErr('linky'); + var isString = angular.isString; + + return function(text, target, attributes) { + if (text == null || text === '') return text; + if (!isString(text)) throw linkyMinErr('notstring', 'Expected string but received: {0}', text); + + var match; + var raw = text; + var html = []; + var url; + var i; + while ((match = raw.match(LINKY_URL_REGEXP))) { + // We can not end in these as they are sometimes found at the end of the sentence + url = match[0]; + // if we did not match ftp/http/www/mailto then assume mailto + if (!match[2] && !match[4]) { + url = (match[3] ? 'http://' : 'mailto:') + url; + } + i = match.index; + addText(raw.substr(0, i)); + addLink(url, match[0].replace(MAILTO_REGEXP, '')); + raw = raw.substring(i + match[0].length); + } + addText(raw); + return $sanitize(html.join('')); + + function addText(text) { + if (!text) { + return; + } + html.push(sanitizeText(text)); + } + + function addLink(url, text) { + var key; + html.push(''); + addText(text); + html.push(''); + } + }; +}]); + + +})(window, window.angular); diff --git a/app/www/lib/ionic/js/angular/angular-sanitize.min.js b/app/www/lib/ionic/js/angular/angular-sanitize.min.js new file mode 100644 index 0000000..2e96280 --- /dev/null +++ b/app/www/lib/ionic/js/angular/angular-sanitize.min.js @@ -0,0 +1,15 @@ +/* + AngularJS v1.5.3 + (c) 2010-2016 Google, Inc. http://angularjs.org + License: MIT +*/ +(function(A,e,B){'use strict';function C(a){var c=[];v(c,e.noop).chars(a);return c.join("")}function h(a,c){var b={},d=a.split(","),l;for(l=0;l=document.documentMode&&n(g);a=g.innerHTML;g.innerHTML=a}while(a!==g.innerHTML);for(b=g.firstChild;b;){switch(b.nodeType){case 1:c.start(b.nodeName.toLowerCase(),E(b.attributes)); +break;case 3:c.chars(b.textContent)}var d;if(!(d=b.firstChild)&&(1==b.nodeType&&c.end(b.nodeName.toLowerCase()),d=b.nextSibling,!d))for(;null==d;){b=b.parentNode;if(b===g)break;d=b.nextSibling;1==b.nodeType&&c.end(b.nodeName.toLowerCase())}b=d}for(;b=g.firstChild;)g.removeChild(b)}function E(a){for(var c={},b=0,d=a.length;b/g,">")}function v(a,c){var b=!1,d=e.bind(a,a.push);return{start:function(a,f){a=e.lowercase(a);!b&&H[a]&&(b=a);b||!0!==t[a]||(d("<"),d(a),e.forEach(f,function(b,f){var g=e.lowercase(f),h="img"===a&&"src"===g||"background"===g;!0!==I[g]||!0===y[g]&&!c(b,h)||(d(" "),d(f),d('="'),d(x(b)),d('"'))}),d(">"))},end:function(a){a=e.lowercase(a);b||!0!==t[a]||!0===z[a]||(d(""));a== +b&&(b=!1)},chars:function(a){b||d(x(a))}}}function n(a){if(a.nodeType===Node.ELEMENT_NODE)for(var c=a.attributes,b=0,d=c.length;b"\u201d\u2019]/i,b=/^mailto:/i,d=e.$$minErr("linky"),g=e.isString;return function(f,h,m){function k(a){a&&p.push(C(a))}function q(a,b){var c;p.push("');k(b);p.push("")}if(null==f||""===f)return f;if(!g(f))throw d("notstring",f);for(var r=f,p=[],s,n;f=r.match(c);)s=f[0],f[2]||f[4]||(s=(f[3]?"http://":"mailto:")+s),n=f.index,k(r.substr(0,n)),q(s,f[0].replace(b,"")),r=r.substring(n+f[0].length);k(r);return a(p.join(""))}}])})(window,window.angular); +//# sourceMappingURL=angular-sanitize.min.js.map diff --git a/app/www/lib/ionic/js/angular/angular.js b/app/www/lib/ionic/js/angular/angular.js new file mode 100644 index 0000000..5e6fb2c --- /dev/null +++ b/app/www/lib/ionic/js/angular/angular.js @@ -0,0 +1,30714 @@ +/** + * @license AngularJS v1.5.3 + * (c) 2010-2016 Google, Inc. http://angularjs.org + * License: MIT + */ +(function(window, document, undefined) {'use strict'; + +/** + * @description + * + * This object provides a utility for producing rich Error messages within + * Angular. It can be called as follows: + * + * var exampleMinErr = minErr('example'); + * throw exampleMinErr('one', 'This {0} is {1}', foo, bar); + * + * The above creates an instance of minErr in the example namespace. The + * resulting error will have a namespaced error code of example.one. The + * resulting error will replace {0} with the value of foo, and {1} with the + * value of bar. The object is not restricted in the number of arguments it can + * take. + * + * If fewer arguments are specified than necessary for interpolation, the extra + * interpolation markers will be preserved in the final string. + * + * Since data will be parsed statically during a build step, some restrictions + * are applied with respect to how minErr instances are created and called. + * Instances should have names of the form namespaceMinErr for a minErr created + * using minErr('namespace') . Error codes, namespaces and template strings + * should all be static strings, not variables or general expressions. + * + * @param {string} module The namespace to use for the new minErr instance. + * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning + * error from returned function, for cases when a particular type of error is useful. + * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance + */ + +function minErr(module, ErrorConstructor) { + ErrorConstructor = ErrorConstructor || Error; + return function() { + var SKIP_INDEXES = 2; + + var templateArgs = arguments, + code = templateArgs[0], + message = '[' + (module ? module + ':' : '') + code + '] ', + template = templateArgs[1], + paramPrefix, i; + + message += template.replace(/\{\d+\}/g, function(match) { + var index = +match.slice(1, -1), + shiftedIndex = index + SKIP_INDEXES; + + if (shiftedIndex < templateArgs.length) { + return toDebugString(templateArgs[shiftedIndex]); + } + + return match; + }); + + message += '\nhttp://errors.angularjs.org/1.5.3/' + + (module ? module + '/' : '') + code; + + for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') { + message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' + + encodeURIComponent(toDebugString(templateArgs[i])); + } + + return new ErrorConstructor(message); + }; +} + +/* We need to tell jshint what variables are being exported */ +/* global angular: true, + msie: true, + jqLite: true, + jQuery: true, + slice: true, + splice: true, + push: true, + toString: true, + ngMinErr: true, + angularModule: true, + uid: true, + REGEX_STRING_REGEXP: true, + VALIDITY_STATE_PROPERTY: true, + + lowercase: true, + uppercase: true, + manualLowercase: true, + manualUppercase: true, + nodeName_: true, + isArrayLike: true, + forEach: true, + forEachSorted: true, + reverseParams: true, + nextUid: true, + setHashKey: true, + extend: true, + toInt: true, + inherit: true, + merge: true, + noop: true, + identity: true, + valueFn: true, + isUndefined: true, + isDefined: true, + isObject: true, + isBlankObject: true, + isString: true, + isNumber: true, + isDate: true, + isArray: true, + isFunction: true, + isRegExp: true, + isWindow: true, + isScope: true, + isFile: true, + isFormData: true, + isBlob: true, + isBoolean: true, + isPromiseLike: true, + trim: true, + escapeForRegexp: true, + isElement: true, + makeMap: true, + includes: true, + arrayRemove: true, + copy: true, + shallowCopy: true, + equals: true, + csp: true, + jq: true, + concat: true, + sliceArgs: true, + bind: true, + toJsonReplacer: true, + toJson: true, + fromJson: true, + convertTimezoneToLocal: true, + timezoneToOffset: true, + startingTag: true, + tryDecodeURIComponent: true, + parseKeyValue: true, + toKeyValue: true, + encodeUriSegment: true, + encodeUriQuery: true, + angularInit: true, + bootstrap: true, + getTestability: true, + snake_case: true, + bindJQuery: true, + assertArg: true, + assertArgFn: true, + assertNotHasOwnProperty: true, + getter: true, + getBlockNodes: true, + hasOwnProperty: true, + createMap: true, + + NODE_TYPE_ELEMENT: true, + NODE_TYPE_ATTRIBUTE: true, + NODE_TYPE_TEXT: true, + NODE_TYPE_COMMENT: true, + NODE_TYPE_DOCUMENT: true, + NODE_TYPE_DOCUMENT_FRAGMENT: true, +*/ + +//////////////////////////////////// + +/** + * @ngdoc module + * @name ng + * @module ng + * @description + * + * # ng (core module) + * The ng module is loaded by default when an AngularJS application is started. The module itself + * contains the essential components for an AngularJS application to function. The table below + * lists a high level breakdown of each of the services/factories, filters, directives and testing + * components available within this core module. + * + *
    + */ + +var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/; + +// The name of a form control's ValidityState property. +// This is used so that it's possible for internal tests to create mock ValidityStates. +var VALIDITY_STATE_PROPERTY = 'validity'; + +var hasOwnProperty = Object.prototype.hasOwnProperty; + +var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;}; +var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;}; + + +var manualLowercase = function(s) { + /* jshint bitwise: false */ + return isString(s) + ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);}) + : s; +}; +var manualUppercase = function(s) { + /* jshint bitwise: false */ + return isString(s) + ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);}) + : s; +}; + + +// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish +// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods +// with correct but slower alternatives. See https://github.com/angular/angular.js/issues/11387 +if ('i' !== 'I'.toLowerCase()) { + lowercase = manualLowercase; + uppercase = manualUppercase; +} + + +var + msie, // holds major version number for IE, or NaN if UA is not IE. + jqLite, // delay binding since jQuery could be loaded after us. + jQuery, // delay binding + slice = [].slice, + splice = [].splice, + push = [].push, + toString = Object.prototype.toString, + getPrototypeOf = Object.getPrototypeOf, + ngMinErr = minErr('ng'), + + /** @name angular */ + angular = window.angular || (window.angular = {}), + angularModule, + uid = 0; + +/** + * documentMode is an IE-only property + * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx + */ +msie = document.documentMode; + + +/** + * @private + * @param {*} obj + * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, + * String ...) + */ +function isArrayLike(obj) { + + // `null`, `undefined` and `window` are not array-like + if (obj == null || isWindow(obj)) return false; + + // arrays, strings and jQuery/jqLite objects are array like + // * jqLite is either the jQuery or jqLite constructor function + // * we have to check the existence of jqLite first as this method is called + // via the forEach method when constructing the jqLite object in the first place + if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true; + + // Support: iOS 8.2 (not reproducible in simulator) + // "length" in obj used to prevent JIT error (gh-11508) + var length = "length" in Object(obj) && obj.length; + + // NodeList objects (with `item` method) and + // other objects with suitable length characteristics are array-like + return isNumber(length) && + (length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item == 'function'); + +} + +/** + * @ngdoc function + * @name angular.forEach + * @module ng + * @kind function + * + * @description + * Invokes the `iterator` function once for each item in `obj` collection, which can be either an + * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value` + * is the value of an object property or an array element, `key` is the object property key or + * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional. + * + * It is worth noting that `.forEach` does not iterate over inherited properties because it filters + * using the `hasOwnProperty` method. + * + * Unlike ES262's + * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18), + * providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just + * return the value provided. + * + ```js + var values = {name: 'misko', gender: 'male'}; + var log = []; + angular.forEach(values, function(value, key) { + this.push(key + ': ' + value); + }, log); + expect(log).toEqual(['name: misko', 'gender: male']); + ``` + * + * @param {Object|Array} obj Object to iterate over. + * @param {Function} iterator Iterator function. + * @param {Object=} context Object to become context (`this`) for the iterator function. + * @returns {Object|Array} Reference to `obj`. + */ + +function forEach(obj, iterator, context) { + var key, length; + if (obj) { + if (isFunction(obj)) { + for (key in obj) { + // Need to check if hasOwnProperty exists, + // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function + if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) { + iterator.call(context, obj[key], key, obj); + } + } + } else if (isArray(obj) || isArrayLike(obj)) { + var isPrimitive = typeof obj !== 'object'; + for (key = 0, length = obj.length; key < length; key++) { + if (isPrimitive || key in obj) { + iterator.call(context, obj[key], key, obj); + } + } + } else if (obj.forEach && obj.forEach !== forEach) { + obj.forEach(iterator, context, obj); + } else if (isBlankObject(obj)) { + // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty + for (key in obj) { + iterator.call(context, obj[key], key, obj); + } + } else if (typeof obj.hasOwnProperty === 'function') { + // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed + for (key in obj) { + if (obj.hasOwnProperty(key)) { + iterator.call(context, obj[key], key, obj); + } + } + } else { + // Slow path for objects which do not have a method `hasOwnProperty` + for (key in obj) { + if (hasOwnProperty.call(obj, key)) { + iterator.call(context, obj[key], key, obj); + } + } + } + } + return obj; +} + +function forEachSorted(obj, iterator, context) { + var keys = Object.keys(obj).sort(); + for (var i = 0; i < keys.length; i++) { + iterator.call(context, obj[keys[i]], keys[i]); + } + return keys; +} + + +/** + * when using forEach the params are value, key, but it is often useful to have key, value. + * @param {function(string, *)} iteratorFn + * @returns {function(*, string)} + */ +function reverseParams(iteratorFn) { + return function(value, key) {iteratorFn(key, value);}; +} + +/** + * A consistent way of creating unique IDs in angular. + * + * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before + * we hit number precision issues in JavaScript. + * + * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M + * + * @returns {number} an unique alpha-numeric string + */ +function nextUid() { + return ++uid; +} + + +/** + * Set or clear the hashkey for an object. + * @param obj object + * @param h the hashkey (!truthy to delete the hashkey) + */ +function setHashKey(obj, h) { + if (h) { + obj.$$hashKey = h; + } else { + delete obj.$$hashKey; + } +} + + +function baseExtend(dst, objs, deep) { + var h = dst.$$hashKey; + + for (var i = 0, ii = objs.length; i < ii; ++i) { + var obj = objs[i]; + if (!isObject(obj) && !isFunction(obj)) continue; + var keys = Object.keys(obj); + for (var j = 0, jj = keys.length; j < jj; j++) { + var key = keys[j]; + var src = obj[key]; + + if (deep && isObject(src)) { + if (isDate(src)) { + dst[key] = new Date(src.valueOf()); + } else if (isRegExp(src)) { + dst[key] = new RegExp(src); + } else if (src.nodeName) { + dst[key] = src.cloneNode(true); + } else if (isElement(src)) { + dst[key] = src.clone(); + } else { + if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {}; + baseExtend(dst[key], [src], true); + } + } else { + dst[key] = src; + } + } + } + + setHashKey(dst, h); + return dst; +} + +/** + * @ngdoc function + * @name angular.extend + * @module ng + * @kind function + * + * @description + * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s) + * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so + * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`. + * + * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use + * {@link angular.merge} for this. + * + * @param {Object} dst Destination object. + * @param {...Object} src Source object(s). + * @returns {Object} Reference to `dst`. + */ +function extend(dst) { + return baseExtend(dst, slice.call(arguments, 1), false); +} + + +/** +* @ngdoc function +* @name angular.merge +* @module ng +* @kind function +* +* @description +* Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s) +* to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so +* by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`. +* +* Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source +* objects, performing a deep copy. +* +* @param {Object} dst Destination object. +* @param {...Object} src Source object(s). +* @returns {Object} Reference to `dst`. +*/ +function merge(dst) { + return baseExtend(dst, slice.call(arguments, 1), true); +} + + + +function toInt(str) { + return parseInt(str, 10); +} + + +function inherit(parent, extra) { + return extend(Object.create(parent), extra); +} + +/** + * @ngdoc function + * @name angular.noop + * @module ng + * @kind function + * + * @description + * A function that performs no operations. This function can be useful when writing code in the + * functional style. + ```js + function foo(callback) { + var result = calculateResult(); + (callback || angular.noop)(result); + } + ``` + */ +function noop() {} +noop.$inject = []; + + +/** + * @ngdoc function + * @name angular.identity + * @module ng + * @kind function + * + * @description + * A function that returns its first argument. This function is useful when writing code in the + * functional style. + * + ```js + function transformer(transformationFn, value) { + return (transformationFn || angular.identity)(value); + }; + ``` + * @param {*} value to be returned. + * @returns {*} the value passed in. + */ +function identity($) {return $;} +identity.$inject = []; + + +function valueFn(value) {return function valueRef() {return value;};} + +function hasCustomToString(obj) { + return isFunction(obj.toString) && obj.toString !== toString; +} + + +/** + * @ngdoc function + * @name angular.isUndefined + * @module ng + * @kind function + * + * @description + * Determines if a reference is undefined. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is undefined. + */ +function isUndefined(value) {return typeof value === 'undefined';} + + +/** + * @ngdoc function + * @name angular.isDefined + * @module ng + * @kind function + * + * @description + * Determines if a reference is defined. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is defined. + */ +function isDefined(value) {return typeof value !== 'undefined';} + + +/** + * @ngdoc function + * @name angular.isObject + * @module ng + * @kind function + * + * @description + * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not + * considered to be objects. Note that JavaScript arrays are objects. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is an `Object` but not `null`. + */ +function isObject(value) { + // http://jsperf.com/isobject4 + return value !== null && typeof value === 'object'; +} + + +/** + * Determine if a value is an object with a null prototype + * + * @returns {boolean} True if `value` is an `Object` with a null prototype + */ +function isBlankObject(value) { + return value !== null && typeof value === 'object' && !getPrototypeOf(value); +} + + +/** + * @ngdoc function + * @name angular.isString + * @module ng + * @kind function + * + * @description + * Determines if a reference is a `String`. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `String`. + */ +function isString(value) {return typeof value === 'string';} + + +/** + * @ngdoc function + * @name angular.isNumber + * @module ng + * @kind function + * + * @description + * Determines if a reference is a `Number`. + * + * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`. + * + * If you wish to exclude these then you can use the native + * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite) + * method. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `Number`. + */ +function isNumber(value) {return typeof value === 'number';} + + +/** + * @ngdoc function + * @name angular.isDate + * @module ng + * @kind function + * + * @description + * Determines if a value is a date. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `Date`. + */ +function isDate(value) { + return toString.call(value) === '[object Date]'; +} + + +/** + * @ngdoc function + * @name angular.isArray + * @module ng + * @kind function + * + * @description + * Determines if a reference is an `Array`. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is an `Array`. + */ +var isArray = Array.isArray; + +/** + * @ngdoc function + * @name angular.isFunction + * @module ng + * @kind function + * + * @description + * Determines if a reference is a `Function`. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `Function`. + */ +function isFunction(value) {return typeof value === 'function';} + + +/** + * Determines if a value is a regular expression object. + * + * @private + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `RegExp`. + */ +function isRegExp(value) { + return toString.call(value) === '[object RegExp]'; +} + + +/** + * Checks if `obj` is a window object. + * + * @private + * @param {*} obj Object to check + * @returns {boolean} True if `obj` is a window obj. + */ +function isWindow(obj) { + return obj && obj.window === obj; +} + + +function isScope(obj) { + return obj && obj.$evalAsync && obj.$watch; +} + + +function isFile(obj) { + return toString.call(obj) === '[object File]'; +} + + +function isFormData(obj) { + return toString.call(obj) === '[object FormData]'; +} + + +function isBlob(obj) { + return toString.call(obj) === '[object Blob]'; +} + + +function isBoolean(value) { + return typeof value === 'boolean'; +} + + +function isPromiseLike(obj) { + return obj && isFunction(obj.then); +} + + +var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/; +function isTypedArray(value) { + return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value)); +} + +function isArrayBuffer(obj) { + return toString.call(obj) === '[object ArrayBuffer]'; +} + + +var trim = function(value) { + return isString(value) ? value.trim() : value; +}; + +// Copied from: +// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021 +// Prereq: s is a string. +var escapeForRegexp = function(s) { + return s.replace(/([-()\[\]{}+?*.$\^|,:#= 0) { + array.splice(index, 1); + } + return index; +} + +/** + * @ngdoc function + * @name angular.copy + * @module ng + * @kind function + * + * @description + * Creates a deep copy of `source`, which should be an object or an array. + * + * * If no destination is supplied, a copy of the object or array is created. + * * If a destination is provided, all of its elements (for arrays) or properties (for objects) + * are deleted and then all elements/properties from the source are copied to it. + * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned. + * * If `source` is identical to 'destination' an exception will be thrown. + * + * @param {*} source The source that will be used to make a copy. + * Can be any type, including primitives, `null`, and `undefined`. + * @param {(Object|Array)=} destination Destination into which the source is copied. If + * provided, must be of the same type as `source`. + * @returns {*} The copy or updated `destination`, if `destination` was specified. + * + * @example + + +
    +
    + Name:
    + E-mail:
    + Gender: male + female
    + + +
    +
    form = {{user | json}}
    +
    master = {{master | json}}
    +
    + + +
    +
    + */ +function copy(source, destination) { + var stackSource = []; + var stackDest = []; + + if (destination) { + if (isTypedArray(destination) || isArrayBuffer(destination)) { + throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated."); + } + if (source === destination) { + throw ngMinErr('cpi', "Can't copy! Source and destination are identical."); + } + + // Empty the destination object + if (isArray(destination)) { + destination.length = 0; + } else { + forEach(destination, function(value, key) { + if (key !== '$$hashKey') { + delete destination[key]; + } + }); + } + + stackSource.push(source); + stackDest.push(destination); + return copyRecurse(source, destination); + } + + return copyElement(source); + + function copyRecurse(source, destination) { + var h = destination.$$hashKey; + var key; + if (isArray(source)) { + for (var i = 0, ii = source.length; i < ii; i++) { + destination.push(copyElement(source[i])); + } + } else if (isBlankObject(source)) { + // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty + for (key in source) { + destination[key] = copyElement(source[key]); + } + } else if (source && typeof source.hasOwnProperty === 'function') { + // Slow path, which must rely on hasOwnProperty + for (key in source) { + if (source.hasOwnProperty(key)) { + destination[key] = copyElement(source[key]); + } + } + } else { + // Slowest path --- hasOwnProperty can't be called as a method + for (key in source) { + if (hasOwnProperty.call(source, key)) { + destination[key] = copyElement(source[key]); + } + } + } + setHashKey(destination, h); + return destination; + } + + function copyElement(source) { + // Simple values + if (!isObject(source)) { + return source; + } + + // Already copied values + var index = stackSource.indexOf(source); + if (index !== -1) { + return stackDest[index]; + } + + if (isWindow(source) || isScope(source)) { + throw ngMinErr('cpws', + "Can't copy! Making copies of Window or Scope instances is not supported."); + } + + var needsRecurse = false; + var destination = copyType(source); + + if (destination === undefined) { + destination = isArray(source) ? [] : Object.create(getPrototypeOf(source)); + needsRecurse = true; + } + + stackSource.push(source); + stackDest.push(destination); + + return needsRecurse + ? copyRecurse(source, destination) + : destination; + } + + function copyType(source) { + switch (toString.call(source)) { + case '[object Int8Array]': + case '[object Int16Array]': + case '[object Int32Array]': + case '[object Float32Array]': + case '[object Float64Array]': + case '[object Uint8Array]': + case '[object Uint8ClampedArray]': + case '[object Uint16Array]': + case '[object Uint32Array]': + return new source.constructor(copyElement(source.buffer)); + + case '[object ArrayBuffer]': + //Support: IE10 + if (!source.slice) { + var copied = new ArrayBuffer(source.byteLength); + new Uint8Array(copied).set(new Uint8Array(source)); + return copied; + } + return source.slice(0); + + case '[object Boolean]': + case '[object Number]': + case '[object String]': + case '[object Date]': + return new source.constructor(source.valueOf()); + + case '[object RegExp]': + var re = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]); + re.lastIndex = source.lastIndex; + return re; + + case '[object Blob]': + return new source.constructor([source], {type: source.type}); + } + + if (isFunction(source.cloneNode)) { + return source.cloneNode(true); + } + } +} + +/** + * Creates a shallow copy of an object, an array or a primitive. + * + * Assumes that there are no proto properties for objects. + */ +function shallowCopy(src, dst) { + if (isArray(src)) { + dst = dst || []; + + for (var i = 0, ii = src.length; i < ii; i++) { + dst[i] = src[i]; + } + } else if (isObject(src)) { + dst = dst || {}; + + for (var key in src) { + if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) { + dst[key] = src[key]; + } + } + } + + return dst || src; +} + + +/** + * @ngdoc function + * @name angular.equals + * @module ng + * @kind function + * + * @description + * Determines if two objects or two values are equivalent. Supports value types, regular + * expressions, arrays and objects. + * + * Two objects or values are considered equivalent if at least one of the following is true: + * + * * Both objects or values pass `===` comparison. + * * Both objects or values are of the same type and all of their properties are equal by + * comparing them with `angular.equals`. + * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal) + * * Both values represent the same regular expression (In JavaScript, + * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual + * representation matches). + * + * During a property comparison, properties of `function` type and properties with names + * that begin with `$` are ignored. + * + * Scope and DOMWindow objects are being compared only by identify (`===`). + * + * @param {*} o1 Object or value to compare. + * @param {*} o2 Object or value to compare. + * @returns {boolean} True if arguments are equal. + */ +function equals(o1, o2) { + if (o1 === o2) return true; + if (o1 === null || o2 === null) return false; + if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN + var t1 = typeof o1, t2 = typeof o2, length, key, keySet; + if (t1 == t2 && t1 == 'object') { + if (isArray(o1)) { + if (!isArray(o2)) return false; + if ((length = o1.length) == o2.length) { + for (key = 0; key < length; key++) { + if (!equals(o1[key], o2[key])) return false; + } + return true; + } + } else if (isDate(o1)) { + if (!isDate(o2)) return false; + return equals(o1.getTime(), o2.getTime()); + } else if (isRegExp(o1)) { + if (!isRegExp(o2)) return false; + return o1.toString() == o2.toString(); + } else { + if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || + isArray(o2) || isDate(o2) || isRegExp(o2)) return false; + keySet = createMap(); + for (key in o1) { + if (key.charAt(0) === '$' || isFunction(o1[key])) continue; + if (!equals(o1[key], o2[key])) return false; + keySet[key] = true; + } + for (key in o2) { + if (!(key in keySet) && + key.charAt(0) !== '$' && + isDefined(o2[key]) && + !isFunction(o2[key])) return false; + } + return true; + } + } + return false; +} + +var csp = function() { + if (!isDefined(csp.rules)) { + + + var ngCspElement = (document.querySelector('[ng-csp]') || + document.querySelector('[data-ng-csp]')); + + if (ngCspElement) { + var ngCspAttribute = ngCspElement.getAttribute('ng-csp') || + ngCspElement.getAttribute('data-ng-csp'); + csp.rules = { + noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1), + noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1) + }; + } else { + csp.rules = { + noUnsafeEval: noUnsafeEval(), + noInlineStyle: false + }; + } + } + + return csp.rules; + + function noUnsafeEval() { + try { + /* jshint -W031, -W054 */ + new Function(''); + /* jshint +W031, +W054 */ + return false; + } catch (e) { + return true; + } + } +}; + +/** + * @ngdoc directive + * @module ng + * @name ngJq + * + * @element ANY + * @param {string=} ngJq the name of the library available under `window` + * to be used for angular.element + * @description + * Use this directive to force the angular.element library. This should be + * used to force either jqLite by leaving ng-jq blank or setting the name of + * the jquery variable under window (eg. jQuery). + * + * Since angular looks for this directive when it is loaded (doesn't wait for the + * DOMContentLoaded event), it must be placed on an element that comes before the script + * which loads angular. Also, only the first instance of `ng-jq` will be used and all + * others ignored. + * + * @example + * This example shows how to force jqLite using the `ngJq` directive to the `html` tag. + ```html + + + ... + ... + + ``` + * @example + * This example shows how to use a jQuery based library of a different name. + * The library name must be available at the top most 'window'. + ```html + + + ... + ... + + ``` + */ +var jq = function() { + if (isDefined(jq.name_)) return jq.name_; + var el; + var i, ii = ngAttrPrefixes.length, prefix, name; + for (i = 0; i < ii; ++i) { + prefix = ngAttrPrefixes[i]; + if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) { + name = el.getAttribute(prefix + 'jq'); + break; + } + } + + return (jq.name_ = name); +}; + +function concat(array1, array2, index) { + return array1.concat(slice.call(array2, index)); +} + +function sliceArgs(args, startIndex) { + return slice.call(args, startIndex || 0); +} + + +/* jshint -W101 */ +/** + * @ngdoc function + * @name angular.bind + * @module ng + * @kind function + * + * @description + * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for + * `fn`). You can supply optional `args` that are prebound to the function. This feature is also + * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as + * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application). + * + * @param {Object} self Context which `fn` should be evaluated in. + * @param {function()} fn Function to be bound. + * @param {...*} args Optional arguments to be prebound to the `fn` function call. + * @returns {function()} Function that wraps the `fn` with all the specified bindings. + */ +/* jshint +W101 */ +function bind(self, fn) { + var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : []; + if (isFunction(fn) && !(fn instanceof RegExp)) { + return curryArgs.length + ? function() { + return arguments.length + ? fn.apply(self, concat(curryArgs, arguments, 0)) + : fn.apply(self, curryArgs); + } + : function() { + return arguments.length + ? fn.apply(self, arguments) + : fn.call(self); + }; + } else { + // in IE, native methods are not functions so they cannot be bound (note: they don't need to be) + return fn; + } +} + + +function toJsonReplacer(key, value) { + var val = value; + + if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') { + val = undefined; + } else if (isWindow(value)) { + val = '$WINDOW'; + } else if (value && document === value) { + val = '$DOCUMENT'; + } else if (isScope(value)) { + val = '$SCOPE'; + } + + return val; +} + + +/** + * @ngdoc function + * @name angular.toJson + * @module ng + * @kind function + * + * @description + * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be + * stripped since angular uses this notation internally. + * + * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON. + * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace. + * If set to an integer, the JSON output will contain that many spaces per indentation. + * @returns {string|undefined} JSON-ified string representing `obj`. + */ +function toJson(obj, pretty) { + if (isUndefined(obj)) return undefined; + if (!isNumber(pretty)) { + pretty = pretty ? 2 : null; + } + return JSON.stringify(obj, toJsonReplacer, pretty); +} + + +/** + * @ngdoc function + * @name angular.fromJson + * @module ng + * @kind function + * + * @description + * Deserializes a JSON string. + * + * @param {string} json JSON string to deserialize. + * @returns {Object|Array|string|number} Deserialized JSON string. + */ +function fromJson(json) { + return isString(json) + ? JSON.parse(json) + : json; +} + + +var ALL_COLONS = /:/g; +function timezoneToOffset(timezone, fallback) { + // IE/Edge do not "understand" colon (`:`) in timezone + timezone = timezone.replace(ALL_COLONS, ''); + var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000; + return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset; +} + + +function addDateMinutes(date, minutes) { + date = new Date(date.getTime()); + date.setMinutes(date.getMinutes() + minutes); + return date; +} + + +function convertTimezoneToLocal(date, timezone, reverse) { + reverse = reverse ? -1 : 1; + var dateTimezoneOffset = date.getTimezoneOffset(); + var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset); + return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset)); +} + + +/** + * @returns {string} Returns the string representation of the element. + */ +function startingTag(element) { + element = jqLite(element).clone(); + try { + // turns out IE does not let you set .html() on elements which + // are not allowed to have children. So we just ignore it. + element.empty(); + } catch (e) {} + var elemHtml = jqLite('
    ').append(element).html(); + try { + return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) : + elemHtml. + match(/^(<[^>]+>)/)[1]. + replace(/^<([\w\-]+)/, function(match, nodeName) {return '<' + lowercase(nodeName);}); + } catch (e) { + return lowercase(elemHtml); + } + +} + + +///////////////////////////////////////////////// + +/** + * Tries to decode the URI component without throwing an exception. + * + * @private + * @param str value potential URI component to check. + * @returns {boolean} True if `value` can be decoded + * with the decodeURIComponent function. + */ +function tryDecodeURIComponent(value) { + try { + return decodeURIComponent(value); + } catch (e) { + // Ignore any invalid uri component + } +} + + +/** + * Parses an escaped url query string into key-value pairs. + * @returns {Object.} + */ +function parseKeyValue(/**string*/keyValue) { + var obj = {}; + forEach((keyValue || "").split('&'), function(keyValue) { + var splitPoint, key, val; + if (keyValue) { + key = keyValue = keyValue.replace(/\+/g,'%20'); + splitPoint = keyValue.indexOf('='); + if (splitPoint !== -1) { + key = keyValue.substring(0, splitPoint); + val = keyValue.substring(splitPoint + 1); + } + key = tryDecodeURIComponent(key); + if (isDefined(key)) { + val = isDefined(val) ? tryDecodeURIComponent(val) : true; + if (!hasOwnProperty.call(obj, key)) { + obj[key] = val; + } else if (isArray(obj[key])) { + obj[key].push(val); + } else { + obj[key] = [obj[key],val]; + } + } + } + }); + return obj; +} + +function toKeyValue(obj) { + var parts = []; + forEach(obj, function(value, key) { + if (isArray(value)) { + forEach(value, function(arrayValue) { + parts.push(encodeUriQuery(key, true) + + (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true))); + }); + } else { + parts.push(encodeUriQuery(key, true) + + (value === true ? '' : '=' + encodeUriQuery(value, true))); + } + }); + return parts.length ? parts.join('&') : ''; +} + + +/** + * We need our custom method because encodeURIComponent is too aggressive and doesn't follow + * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path + * segments: + * segment = *pchar + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + * pct-encoded = "%" HEXDIG HEXDIG + * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + * / "*" / "+" / "," / ";" / "=" + */ +function encodeUriSegment(val) { + return encodeUriQuery(val, true). + replace(/%26/gi, '&'). + replace(/%3D/gi, '='). + replace(/%2B/gi, '+'); +} + + +/** + * This method is intended for encoding *key* or *value* parts of query component. We need a custom + * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be + * encoded per http://tools.ietf.org/html/rfc3986: + * query = *( pchar / "/" / "?" ) + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + * pct-encoded = "%" HEXDIG HEXDIG + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + * / "*" / "+" / "," / ";" / "=" + */ +function encodeUriQuery(val, pctEncodeSpaces) { + return encodeURIComponent(val). + replace(/%40/gi, '@'). + replace(/%3A/gi, ':'). + replace(/%24/g, '$'). + replace(/%2C/gi, ','). + replace(/%3B/gi, ';'). + replace(/%20/g, (pctEncodeSpaces ? '%20' : '+')); +} + +var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-']; + +function getNgAttribute(element, ngAttr) { + var attr, i, ii = ngAttrPrefixes.length; + for (i = 0; i < ii; ++i) { + attr = ngAttrPrefixes[i] + ngAttr; + if (isString(attr = element.getAttribute(attr))) { + return attr; + } + } + return null; +} + +/** + * @ngdoc directive + * @name ngApp + * @module ng + * + * @element ANY + * @param {angular.Module} ngApp an optional application + * {@link angular.module module} name to load. + * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be + * created in "strict-di" mode. This means that the application will fail to invoke functions which + * do not use explicit function annotation (and are thus unsuitable for minification), as described + * in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in + * tracking down the root of these bugs. + * + * @description + * + * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive + * designates the **root element** of the application and is typically placed near the root element + * of the page - e.g. on the `` or `` tags. + * + * There are a few things to keep in mind when using `ngApp`: + * - only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp` + * found in the document will be used to define the root element to auto-bootstrap as an + * application. To run multiple applications in an HTML document you must manually bootstrap them using + * {@link angular.bootstrap} instead. + * - AngularJS applications cannot be nested within each other. + * - Do not use a directive that uses {@link ng.$compile#transclusion transclusion} on the same element as `ngApp`. + * This includes directives such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and + * {@link ngRoute.ngView `ngView`}. + * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector}, + * causing animations to stop working and making the injector inaccessible from outside the app. + * + * You can specify an **AngularJS module** to be used as the root module for the application. This + * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It + * should contain the application code needed or have dependencies on other modules that will + * contain the code. See {@link angular.module} for more information. + * + * In the example below if the `ngApp` directive were not placed on the `html` element then the + * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}` + * would not be resolved to `3`. + * + * `ngApp` is the easiest, and most common way to bootstrap an application. + * + + +
    + I can add: {{a}} + {{b}} = {{ a+b }} +
    +
    + + angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) { + $scope.a = 1; + $scope.b = 2; + }); + +
    + * + * Using `ngStrictDi`, you would see something like this: + * + + +
    +
    + I can add: {{a}} + {{b}} = {{ a+b }} + +

    This renders because the controller does not fail to + instantiate, by using explicit annotation style (see + script.js for details) +

    +
    + +
    + Name:
    + Hello, {{name}}! + +

    This renders because the controller does not fail to + instantiate, by using explicit annotation style + (see script.js for details) +

    +
    + +
    + I can add: {{a}} + {{b}} = {{ a+b }} + +

    The controller could not be instantiated, due to relying + on automatic function annotations (which are disabled in + strict mode). As such, the content of this section is not + interpolated, and there should be an error in your web console. +

    +
    +
    +
    + + angular.module('ngAppStrictDemo', []) + // BadController will fail to instantiate, due to relying on automatic function annotation, + // rather than an explicit annotation + .controller('BadController', function($scope) { + $scope.a = 1; + $scope.b = 2; + }) + // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated, + // due to using explicit annotations using the array style and $inject property, respectively. + .controller('GoodController1', ['$scope', function($scope) { + $scope.a = 1; + $scope.b = 2; + }]) + .controller('GoodController2', GoodController2); + function GoodController2($scope) { + $scope.name = "World"; + } + GoodController2.$inject = ['$scope']; + + + div[ng-controller] { + margin-bottom: 1em; + -webkit-border-radius: 4px; + border-radius: 4px; + border: 1px solid; + padding: .5em; + } + div[ng-controller^=Good] { + border-color: #d6e9c6; + background-color: #dff0d8; + color: #3c763d; + } + div[ng-controller^=Bad] { + border-color: #ebccd1; + background-color: #f2dede; + color: #a94442; + margin-bottom: 0; + } + +
    + */ +function angularInit(element, bootstrap) { + var appElement, + module, + config = {}; + + // The element `element` has priority over any other element + forEach(ngAttrPrefixes, function(prefix) { + var name = prefix + 'app'; + + if (!appElement && element.hasAttribute && element.hasAttribute(name)) { + appElement = element; + module = element.getAttribute(name); + } + }); + forEach(ngAttrPrefixes, function(prefix) { + var name = prefix + 'app'; + var candidate; + + if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) { + appElement = candidate; + module = candidate.getAttribute(name); + } + }); + if (appElement) { + config.strictDi = getNgAttribute(appElement, "strict-di") !== null; + bootstrap(appElement, module ? [module] : [], config); + } +} + +/** + * @ngdoc function + * @name angular.bootstrap + * @module ng + * @description + * Use this function to manually start up angular application. + * + * For more information, see the {@link guide/bootstrap Bootstrap guide}. + * + * Angular will detect if it has been loaded into the browser more than once and only allow the + * first loaded script to be bootstrapped and will report a warning to the browser console for + * each of the subsequent scripts. This prevents strange results in applications, where otherwise + * multiple instances of Angular try to work on the DOM. + * + *
    + * **Note:** Protractor based end-to-end tests cannot use this function to bootstrap manually. + * They must use {@link ng.directive:ngApp ngApp}. + *
    + * + *
    + * **Note:** Do not bootstrap the app on an element with a directive that uses {@link ng.$compile#transclusion transclusion}, + * such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and {@link ngRoute.ngView `ngView`}. + * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector}, + * causing animations to stop working and making the injector inaccessible from outside the app. + *
    + * + * ```html + * + * + * + *
    + * {{greeting}} + *
    + * + * + * + * + * + * ``` + * + * @param {DOMElement} element DOM element which is the root of angular application. + * @param {Array=} modules an array of modules to load into the application. + * Each item in the array should be the name of a predefined module or a (DI annotated) + * function that will be invoked by the injector as a `config` block. + * See: {@link angular.module modules} + * @param {Object=} config an object for defining configuration options for the application. The + * following keys are supported: + * + * * `strictDi` - disable automatic function annotation for the application. This is meant to + * assist in finding bugs which break minified code. Defaults to `false`. + * + * @returns {auto.$injector} Returns the newly created injector for this app. + */ +function bootstrap(element, modules, config) { + if (!isObject(config)) config = {}; + var defaultConfig = { + strictDi: false + }; + config = extend(defaultConfig, config); + var doBootstrap = function() { + element = jqLite(element); + + if (element.injector()) { + var tag = (element[0] === document) ? 'document' : startingTag(element); + //Encode angle brackets to prevent input from being sanitized to empty string #8683 + throw ngMinErr( + 'btstrpd', + "App Already Bootstrapped with this Element '{0}'", + tag.replace(//,'>')); + } + + modules = modules || []; + modules.unshift(['$provide', function($provide) { + $provide.value('$rootElement', element); + }]); + + if (config.debugInfoEnabled) { + // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`. + modules.push(['$compileProvider', function($compileProvider) { + $compileProvider.debugInfoEnabled(true); + }]); + } + + modules.unshift('ng'); + var injector = createInjector(modules, config.strictDi); + injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', + function bootstrapApply(scope, element, compile, injector) { + scope.$apply(function() { + element.data('$injector', injector); + compile(element)(scope); + }); + }] + ); + return injector; + }; + + var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/; + var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/; + + if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) { + config.debugInfoEnabled = true; + window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, ''); + } + + if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) { + return doBootstrap(); + } + + window.name = window.name.replace(NG_DEFER_BOOTSTRAP, ''); + angular.resumeBootstrap = function(extraModules) { + forEach(extraModules, function(module) { + modules.push(module); + }); + return doBootstrap(); + }; + + if (isFunction(angular.resumeDeferredBootstrap)) { + angular.resumeDeferredBootstrap(); + } +} + +/** + * @ngdoc function + * @name angular.reloadWithDebugInfo + * @module ng + * @description + * Use this function to reload the current application with debug information turned on. + * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`. + * + * See {@link ng.$compileProvider#debugInfoEnabled} for more. + */ +function reloadWithDebugInfo() { + window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name; + window.location.reload(); +} + +/** + * @name angular.getTestability + * @module ng + * @description + * Get the testability service for the instance of Angular on the given + * element. + * @param {DOMElement} element DOM element which is the root of angular application. + */ +function getTestability(rootElement) { + var injector = angular.element(rootElement).injector(); + if (!injector) { + throw ngMinErr('test', + 'no injector found for element argument to getTestability'); + } + return injector.get('$$testability'); +} + +var SNAKE_CASE_REGEXP = /[A-Z]/g; +function snake_case(name, separator) { + separator = separator || '_'; + return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) { + return (pos ? separator : '') + letter.toLowerCase(); + }); +} + +var bindJQueryFired = false; +function bindJQuery() { + var originalCleanData; + + if (bindJQueryFired) { + return; + } + + // bind to jQuery if present; + var jqName = jq(); + jQuery = isUndefined(jqName) ? window.jQuery : // use jQuery (if present) + !jqName ? undefined : // use jqLite + window[jqName]; // use jQuery specified by `ngJq` + + // Use jQuery if it exists with proper functionality, otherwise default to us. + // Angular 1.2+ requires jQuery 1.7+ for on()/off() support. + // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older + // versions. It will not work for sure with jQuery <1.7, though. + if (jQuery && jQuery.fn.on) { + jqLite = jQuery; + extend(jQuery.fn, { + scope: JQLitePrototype.scope, + isolateScope: JQLitePrototype.isolateScope, + controller: JQLitePrototype.controller, + injector: JQLitePrototype.injector, + inheritedData: JQLitePrototype.inheritedData + }); + + // All nodes removed from the DOM via various jQuery APIs like .remove() + // are passed through jQuery.cleanData. Monkey-patch this method to fire + // the $destroy event on all removed nodes. + originalCleanData = jQuery.cleanData; + jQuery.cleanData = function(elems) { + var events; + for (var i = 0, elem; (elem = elems[i]) != null; i++) { + events = jQuery._data(elem, "events"); + if (events && events.$destroy) { + jQuery(elem).triggerHandler('$destroy'); + } + } + originalCleanData(elems); + }; + } else { + jqLite = JQLite; + } + + angular.element = jqLite; + + // Prevent double-proxying. + bindJQueryFired = true; +} + +/** + * throw error if the argument is falsy. + */ +function assertArg(arg, name, reason) { + if (!arg) { + throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required")); + } + return arg; +} + +function assertArgFn(arg, name, acceptArrayAnnotation) { + if (acceptArrayAnnotation && isArray(arg)) { + arg = arg[arg.length - 1]; + } + + assertArg(isFunction(arg), name, 'not a function, got ' + + (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg)); + return arg; +} + +/** + * throw error if the name given is hasOwnProperty + * @param {String} name the name to test + * @param {String} context the context in which the name is used, such as module or directive + */ +function assertNotHasOwnProperty(name, context) { + if (name === 'hasOwnProperty') { + throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context); + } +} + +/** + * Return the value accessible from the object by path. Any undefined traversals are ignored + * @param {Object} obj starting object + * @param {String} path path to traverse + * @param {boolean} [bindFnToScope=true] + * @returns {Object} value as accessible by path + */ +//TODO(misko): this function needs to be removed +function getter(obj, path, bindFnToScope) { + if (!path) return obj; + var keys = path.split('.'); + var key; + var lastInstance = obj; + var len = keys.length; + + for (var i = 0; i < len; i++) { + key = keys[i]; + if (obj) { + obj = (lastInstance = obj)[key]; + } + } + if (!bindFnToScope && isFunction(obj)) { + return bind(lastInstance, obj); + } + return obj; +} + +/** + * Return the DOM siblings between the first and last node in the given array. + * @param {Array} array like object + * @returns {Array} the inputted object or a jqLite collection containing the nodes + */ +function getBlockNodes(nodes) { + // TODO(perf): update `nodes` instead of creating a new object? + var node = nodes[0]; + var endNode = nodes[nodes.length - 1]; + var blockNodes; + + for (var i = 1; node !== endNode && (node = node.nextSibling); i++) { + if (blockNodes || nodes[i] !== node) { + if (!blockNodes) { + blockNodes = jqLite(slice.call(nodes, 0, i)); + } + blockNodes.push(node); + } + } + + return blockNodes || nodes; +} + + +/** + * Creates a new object without a prototype. This object is useful for lookup without having to + * guard against prototypically inherited properties via hasOwnProperty. + * + * Related micro-benchmarks: + * - http://jsperf.com/object-create2 + * - http://jsperf.com/proto-map-lookup/2 + * - http://jsperf.com/for-in-vs-object-keys2 + * + * @returns {Object} + */ +function createMap() { + return Object.create(null); +} + +var NODE_TYPE_ELEMENT = 1; +var NODE_TYPE_ATTRIBUTE = 2; +var NODE_TYPE_TEXT = 3; +var NODE_TYPE_COMMENT = 8; +var NODE_TYPE_DOCUMENT = 9; +var NODE_TYPE_DOCUMENT_FRAGMENT = 11; + +/** + * @ngdoc type + * @name angular.Module + * @module ng + * @description + * + * Interface for configuring angular {@link angular.module modules}. + */ + +function setupModuleLoader(window) { + + var $injectorMinErr = minErr('$injector'); + var ngMinErr = minErr('ng'); + + function ensure(obj, name, factory) { + return obj[name] || (obj[name] = factory()); + } + + var angular = ensure(window, 'angular', Object); + + // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap + angular.$$minErr = angular.$$minErr || minErr; + + return ensure(angular, 'module', function() { + /** @type {Object.} */ + var modules = {}; + + /** + * @ngdoc function + * @name angular.module + * @module ng + * @description + * + * The `angular.module` is a global place for creating, registering and retrieving Angular + * modules. + * All modules (angular core or 3rd party) that should be available to an application must be + * registered using this mechanism. + * + * Passing one argument retrieves an existing {@link angular.Module}, + * whereas passing more than one argument creates a new {@link angular.Module} + * + * + * # Module + * + * A module is a collection of services, directives, controllers, filters, and configuration information. + * `angular.module` is used to configure the {@link auto.$injector $injector}. + * + * ```js + * // Create a new module + * var myModule = angular.module('myModule', []); + * + * // register a new service + * myModule.value('appName', 'MyCoolApp'); + * + * // configure existing services inside initialization blocks. + * myModule.config(['$locationProvider', function($locationProvider) { + * // Configure existing providers + * $locationProvider.hashPrefix('!'); + * }]); + * ``` + * + * Then you can create an injector and load your modules like this: + * + * ```js + * var injector = angular.injector(['ng', 'myModule']) + * ``` + * + * However it's more likely that you'll just use + * {@link ng.directive:ngApp ngApp} or + * {@link angular.bootstrap} to simplify this process for you. + * + * @param {!string} name The name of the module to create or retrieve. + * @param {!Array.=} requires If specified then new module is being created. If + * unspecified then the module is being retrieved for further configuration. + * @param {Function=} configFn Optional configuration function for the module. Same as + * {@link angular.Module#config Module#config()}. + * @returns {angular.Module} new module with the {@link angular.Module} api. + */ + return function module(name, requires, configFn) { + var assertNotHasOwnProperty = function(name, context) { + if (name === 'hasOwnProperty') { + throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context); + } + }; + + assertNotHasOwnProperty(name, 'module'); + if (requires && modules.hasOwnProperty(name)) { + modules[name] = null; + } + return ensure(modules, name, function() { + if (!requires) { + throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " + + "the module name or forgot to load it. If registering a module ensure that you " + + "specify the dependencies as the second argument.", name); + } + + /** @type {!Array.>} */ + var invokeQueue = []; + + /** @type {!Array.} */ + var configBlocks = []; + + /** @type {!Array.} */ + var runBlocks = []; + + var config = invokeLater('$injector', 'invoke', 'push', configBlocks); + + /** @type {angular.Module} */ + var moduleInstance = { + // Private state + _invokeQueue: invokeQueue, + _configBlocks: configBlocks, + _runBlocks: runBlocks, + + /** + * @ngdoc property + * @name angular.Module#requires + * @module ng + * + * @description + * Holds the list of modules which the injector will load before the current module is + * loaded. + */ + requires: requires, + + /** + * @ngdoc property + * @name angular.Module#name + * @module ng + * + * @description + * Name of the module. + */ + name: name, + + + /** + * @ngdoc method + * @name angular.Module#provider + * @module ng + * @param {string} name service name + * @param {Function} providerType Construction function for creating new instance of the + * service. + * @description + * See {@link auto.$provide#provider $provide.provider()}. + */ + provider: invokeLaterAndSetModuleName('$provide', 'provider'), + + /** + * @ngdoc method + * @name angular.Module#factory + * @module ng + * @param {string} name service name + * @param {Function} providerFunction Function for creating new instance of the service. + * @description + * See {@link auto.$provide#factory $provide.factory()}. + */ + factory: invokeLaterAndSetModuleName('$provide', 'factory'), + + /** + * @ngdoc method + * @name angular.Module#service + * @module ng + * @param {string} name service name + * @param {Function} constructor A constructor function that will be instantiated. + * @description + * See {@link auto.$provide#service $provide.service()}. + */ + service: invokeLaterAndSetModuleName('$provide', 'service'), + + /** + * @ngdoc method + * @name angular.Module#value + * @module ng + * @param {string} name service name + * @param {*} object Service instance object. + * @description + * See {@link auto.$provide#value $provide.value()}. + */ + value: invokeLater('$provide', 'value'), + + /** + * @ngdoc method + * @name angular.Module#constant + * @module ng + * @param {string} name constant name + * @param {*} object Constant value. + * @description + * Because the constants are fixed, they get applied before other provide methods. + * See {@link auto.$provide#constant $provide.constant()}. + */ + constant: invokeLater('$provide', 'constant', 'unshift'), + + /** + * @ngdoc method + * @name angular.Module#decorator + * @module ng + * @param {string} The name of the service to decorate. + * @param {Function} This function will be invoked when the service needs to be + * instantiated and should return the decorated service instance. + * @description + * See {@link auto.$provide#decorator $provide.decorator()}. + */ + decorator: invokeLaterAndSetModuleName('$provide', 'decorator'), + + /** + * @ngdoc method + * @name angular.Module#animation + * @module ng + * @param {string} name animation name + * @param {Function} animationFactory Factory function for creating new instance of an + * animation. + * @description + * + * **NOTE**: animations take effect only if the **ngAnimate** module is loaded. + * + * + * Defines an animation hook that can be later used with + * {@link $animate $animate} service and directives that use this service. + * + * ```js + * module.animation('.animation-name', function($inject1, $inject2) { + * return { + * eventName : function(element, done) { + * //code to run the animation + * //once complete, then run done() + * return function cancellationFunction(element) { + * //code to cancel the animation + * } + * } + * } + * }) + * ``` + * + * See {@link ng.$animateProvider#register $animateProvider.register()} and + * {@link ngAnimate ngAnimate module} for more information. + */ + animation: invokeLaterAndSetModuleName('$animateProvider', 'register'), + + /** + * @ngdoc method + * @name angular.Module#filter + * @module ng + * @param {string} name Filter name - this must be a valid angular expression identifier + * @param {Function} filterFactory Factory function for creating new instance of filter. + * @description + * See {@link ng.$filterProvider#register $filterProvider.register()}. + * + *
    + * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`. + * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace + * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores + * (`myapp_subsection_filterx`). + *
    + */ + filter: invokeLaterAndSetModuleName('$filterProvider', 'register'), + + /** + * @ngdoc method + * @name angular.Module#controller + * @module ng + * @param {string|Object} name Controller name, or an object map of controllers where the + * keys are the names and the values are the constructors. + * @param {Function} constructor Controller constructor function. + * @description + * See {@link ng.$controllerProvider#register $controllerProvider.register()}. + */ + controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'), + + /** + * @ngdoc method + * @name angular.Module#directive + * @module ng + * @param {string|Object} name Directive name, or an object map of directives where the + * keys are the names and the values are the factories. + * @param {Function} directiveFactory Factory function for creating new instance of + * directives. + * @description + * See {@link ng.$compileProvider#directive $compileProvider.directive()}. + */ + directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'), + + /** + * @ngdoc method + * @name angular.Module#component + * @module ng + * @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp) + * @param {Object} options Component definition object (a simplified + * {@link ng.$compile#directive-definition-object directive definition object}) + * + * @description + * See {@link ng.$compileProvider#component $compileProvider.component()}. + */ + component: invokeLaterAndSetModuleName('$compileProvider', 'component'), + + /** + * @ngdoc method + * @name angular.Module#config + * @module ng + * @param {Function} configFn Execute this function on module load. Useful for service + * configuration. + * @description + * Use this method to register work which needs to be performed on module loading. + * For more about how to configure services, see + * {@link providers#provider-recipe Provider Recipe}. + */ + config: config, + + /** + * @ngdoc method + * @name angular.Module#run + * @module ng + * @param {Function} initializationFn Execute this function after injector creation. + * Useful for application initialization. + * @description + * Use this method to register work which should be performed when the injector is done + * loading all modules. + */ + run: function(block) { + runBlocks.push(block); + return this; + } + }; + + if (configFn) { + config(configFn); + } + + return moduleInstance; + + /** + * @param {string} provider + * @param {string} method + * @param {String=} insertMethod + * @returns {angular.Module} + */ + function invokeLater(provider, method, insertMethod, queue) { + if (!queue) queue = invokeQueue; + return function() { + queue[insertMethod || 'push']([provider, method, arguments]); + return moduleInstance; + }; + } + + /** + * @param {string} provider + * @param {string} method + * @returns {angular.Module} + */ + function invokeLaterAndSetModuleName(provider, method) { + return function(recipeName, factoryFunction) { + if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name; + invokeQueue.push([provider, method, arguments]); + return moduleInstance; + }; + } + }); + }; + }); + +} + +/* global: toDebugString: true */ + +function serializeObject(obj) { + var seen = []; + + return JSON.stringify(obj, function(key, val) { + val = toJsonReplacer(key, val); + if (isObject(val)) { + + if (seen.indexOf(val) >= 0) return '...'; + + seen.push(val); + } + return val; + }); +} + +function toDebugString(obj) { + if (typeof obj === 'function') { + return obj.toString().replace(/ \{[\s\S]*$/, ''); + } else if (isUndefined(obj)) { + return 'undefined'; + } else if (typeof obj !== 'string') { + return serializeObject(obj); + } + return obj; +} + +/* global angularModule: true, + version: true, + + $CompileProvider, + + htmlAnchorDirective, + inputDirective, + inputDirective, + formDirective, + scriptDirective, + selectDirective, + styleDirective, + optionDirective, + ngBindDirective, + ngBindHtmlDirective, + ngBindTemplateDirective, + ngClassDirective, + ngClassEvenDirective, + ngClassOddDirective, + ngCloakDirective, + ngControllerDirective, + ngFormDirective, + ngHideDirective, + ngIfDirective, + ngIncludeDirective, + ngIncludeFillContentDirective, + ngInitDirective, + ngNonBindableDirective, + ngPluralizeDirective, + ngRepeatDirective, + ngShowDirective, + ngStyleDirective, + ngSwitchDirective, + ngSwitchWhenDirective, + ngSwitchDefaultDirective, + ngOptionsDirective, + ngTranscludeDirective, + ngModelDirective, + ngListDirective, + ngChangeDirective, + patternDirective, + patternDirective, + requiredDirective, + requiredDirective, + minlengthDirective, + minlengthDirective, + maxlengthDirective, + maxlengthDirective, + ngValueDirective, + ngModelOptionsDirective, + ngAttributeAliasDirectives, + ngEventDirectives, + + $AnchorScrollProvider, + $AnimateProvider, + $CoreAnimateCssProvider, + $$CoreAnimateJsProvider, + $$CoreAnimateQueueProvider, + $$AnimateRunnerFactoryProvider, + $$AnimateAsyncRunFactoryProvider, + $BrowserProvider, + $CacheFactoryProvider, + $ControllerProvider, + $DateProvider, + $DocumentProvider, + $ExceptionHandlerProvider, + $FilterProvider, + $$ForceReflowProvider, + $InterpolateProvider, + $IntervalProvider, + $$HashMapProvider, + $HttpProvider, + $HttpParamSerializerProvider, + $HttpParamSerializerJQLikeProvider, + $HttpBackendProvider, + $xhrFactoryProvider, + $LocationProvider, + $LogProvider, + $ParseProvider, + $RootScopeProvider, + $QProvider, + $$QProvider, + $$SanitizeUriProvider, + $SceProvider, + $SceDelegateProvider, + $SnifferProvider, + $TemplateCacheProvider, + $TemplateRequestProvider, + $$TestabilityProvider, + $TimeoutProvider, + $$RAFProvider, + $WindowProvider, + $$jqLiteProvider, + $$CookieReaderProvider +*/ + + +/** + * @ngdoc object + * @name angular.version + * @module ng + * @description + * An object that contains information about the current AngularJS version. + * + * This object has the following properties: + * + * - `full` – `{string}` – Full version string, such as "0.9.18". + * - `major` – `{number}` – Major version number, such as "0". + * - `minor` – `{number}` – Minor version number, such as "9". + * - `dot` – `{number}` – Dot version number, such as "18". + * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". + */ +var version = { + full: '1.5.3', // all of these placeholder strings will be replaced by grunt's + major: 1, // package task + minor: 5, + dot: 3, + codeName: 'diplohaplontic-meiosis' +}; + + +function publishExternalAPI(angular) { + extend(angular, { + 'bootstrap': bootstrap, + 'copy': copy, + 'extend': extend, + 'merge': merge, + 'equals': equals, + 'element': jqLite, + 'forEach': forEach, + 'injector': createInjector, + 'noop': noop, + 'bind': bind, + 'toJson': toJson, + 'fromJson': fromJson, + 'identity': identity, + 'isUndefined': isUndefined, + 'isDefined': isDefined, + 'isString': isString, + 'isFunction': isFunction, + 'isObject': isObject, + 'isNumber': isNumber, + 'isElement': isElement, + 'isArray': isArray, + 'version': version, + 'isDate': isDate, + 'lowercase': lowercase, + 'uppercase': uppercase, + 'callbacks': {counter: 0}, + 'getTestability': getTestability, + '$$minErr': minErr, + '$$csp': csp, + 'reloadWithDebugInfo': reloadWithDebugInfo + }); + + angularModule = setupModuleLoader(window); + + angularModule('ng', ['ngLocale'], ['$provide', + function ngModule($provide) { + // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it. + $provide.provider({ + $$sanitizeUri: $$SanitizeUriProvider + }); + $provide.provider('$compile', $CompileProvider). + directive({ + a: htmlAnchorDirective, + input: inputDirective, + textarea: inputDirective, + form: formDirective, + script: scriptDirective, + select: selectDirective, + style: styleDirective, + option: optionDirective, + ngBind: ngBindDirective, + ngBindHtml: ngBindHtmlDirective, + ngBindTemplate: ngBindTemplateDirective, + ngClass: ngClassDirective, + ngClassEven: ngClassEvenDirective, + ngClassOdd: ngClassOddDirective, + ngCloak: ngCloakDirective, + ngController: ngControllerDirective, + ngForm: ngFormDirective, + ngHide: ngHideDirective, + ngIf: ngIfDirective, + ngInclude: ngIncludeDirective, + ngInit: ngInitDirective, + ngNonBindable: ngNonBindableDirective, + ngPluralize: ngPluralizeDirective, + ngRepeat: ngRepeatDirective, + ngShow: ngShowDirective, + ngStyle: ngStyleDirective, + ngSwitch: ngSwitchDirective, + ngSwitchWhen: ngSwitchWhenDirective, + ngSwitchDefault: ngSwitchDefaultDirective, + ngOptions: ngOptionsDirective, + ngTransclude: ngTranscludeDirective, + ngModel: ngModelDirective, + ngList: ngListDirective, + ngChange: ngChangeDirective, + pattern: patternDirective, + ngPattern: patternDirective, + required: requiredDirective, + ngRequired: requiredDirective, + minlength: minlengthDirective, + ngMinlength: minlengthDirective, + maxlength: maxlengthDirective, + ngMaxlength: maxlengthDirective, + ngValue: ngValueDirective, + ngModelOptions: ngModelOptionsDirective + }). + directive({ + ngInclude: ngIncludeFillContentDirective + }). + directive(ngAttributeAliasDirectives). + directive(ngEventDirectives); + $provide.provider({ + $anchorScroll: $AnchorScrollProvider, + $animate: $AnimateProvider, + $animateCss: $CoreAnimateCssProvider, + $$animateJs: $$CoreAnimateJsProvider, + $$animateQueue: $$CoreAnimateQueueProvider, + $$AnimateRunner: $$AnimateRunnerFactoryProvider, + $$animateAsyncRun: $$AnimateAsyncRunFactoryProvider, + $browser: $BrowserProvider, + $cacheFactory: $CacheFactoryProvider, + $controller: $ControllerProvider, + $document: $DocumentProvider, + $exceptionHandler: $ExceptionHandlerProvider, + $filter: $FilterProvider, + $$forceReflow: $$ForceReflowProvider, + $interpolate: $InterpolateProvider, + $interval: $IntervalProvider, + $http: $HttpProvider, + $httpParamSerializer: $HttpParamSerializerProvider, + $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider, + $httpBackend: $HttpBackendProvider, + $xhrFactory: $xhrFactoryProvider, + $location: $LocationProvider, + $log: $LogProvider, + $parse: $ParseProvider, + $rootScope: $RootScopeProvider, + $q: $QProvider, + $$q: $$QProvider, + $sce: $SceProvider, + $sceDelegate: $SceDelegateProvider, + $sniffer: $SnifferProvider, + $templateCache: $TemplateCacheProvider, + $templateRequest: $TemplateRequestProvider, + $$testability: $$TestabilityProvider, + $timeout: $TimeoutProvider, + $window: $WindowProvider, + $$rAF: $$RAFProvider, + $$jqLite: $$jqLiteProvider, + $$HashMap: $$HashMapProvider, + $$cookieReader: $$CookieReaderProvider + }); + } + ]); +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Any commits to this file should be reviewed with security in mind. * + * Changes to this file can potentially create security vulnerabilities. * + * An approval from 2 Core members with history of modifying * + * this file is required. * + * * + * Does the change somehow allow for arbitrary javascript to be executed? * + * Or allows for someone to change the prototype of built-in objects? * + * Or gives undesired access to variables likes document or window? * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* global JQLitePrototype: true, + addEventListenerFn: true, + removeEventListenerFn: true, + BOOLEAN_ATTR: true, + ALIASED_ATTR: true, +*/ + +////////////////////////////////// +//JQLite +////////////////////////////////// + +/** + * @ngdoc function + * @name angular.element + * @module ng + * @kind function + * + * @description + * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element. + * + * If jQuery is available, `angular.element` is an alias for the + * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element` + * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**. + * + * jqLite is a tiny, API-compatible subset of jQuery that allows + * Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most + * commonly needed functionality with the goal of having a very small footprint. + * + * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the + * {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a + * specific version of jQuery if multiple versions exist on the page. + * + *
    **Note:** All element references in Angular are always wrapped with jQuery or + * jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.
    + * + *
    **Note:** Keep in mind that this function will not find elements + * by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)` + * or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.
    + * + * ## Angular's jqLite + * jqLite provides only the following jQuery methods: + * + * - [`addClass()`](http://api.jquery.com/addClass/) + * - [`after()`](http://api.jquery.com/after/) + * - [`append()`](http://api.jquery.com/append/) + * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters + * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData + * - [`children()`](http://api.jquery.com/children/) - Does not support selectors + * - [`clone()`](http://api.jquery.com/clone/) + * - [`contents()`](http://api.jquery.com/contents/) + * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`. + * As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing. + * - [`data()`](http://api.jquery.com/data/) + * - [`detach()`](http://api.jquery.com/detach/) + * - [`empty()`](http://api.jquery.com/empty/) + * - [`eq()`](http://api.jquery.com/eq/) + * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name + * - [`hasClass()`](http://api.jquery.com/hasClass/) + * - [`html()`](http://api.jquery.com/html/) + * - [`next()`](http://api.jquery.com/next/) - Does not support selectors + * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData + * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter + * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors + * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors + * - [`prepend()`](http://api.jquery.com/prepend/) + * - [`prop()`](http://api.jquery.com/prop/) + * - [`ready()`](http://api.jquery.com/ready/) + * - [`remove()`](http://api.jquery.com/remove/) + * - [`removeAttr()`](http://api.jquery.com/removeAttr/) + * - [`removeClass()`](http://api.jquery.com/removeClass/) + * - [`removeData()`](http://api.jquery.com/removeData/) + * - [`replaceWith()`](http://api.jquery.com/replaceWith/) + * - [`text()`](http://api.jquery.com/text/) + * - [`toggleClass()`](http://api.jquery.com/toggleClass/) + * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers. + * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter + * - [`val()`](http://api.jquery.com/val/) + * - [`wrap()`](http://api.jquery.com/wrap/) + * + * ## jQuery/jqLite Extras + * Angular also provides the following additional methods and events to both jQuery and jqLite: + * + * ### Events + * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event + * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM + * element before it is removed. + * + * ### Methods + * - `controller(name)` - retrieves the controller of the current element or its parent. By default + * retrieves controller associated with the `ngController` directive. If `name` is provided as + * camelCase directive name, then the controller for this directive will be retrieved (e.g. + * `'ngModel'`). + * - `injector()` - retrieves the injector of the current element or its parent. + * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current + * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to + * be enabled. + * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the + * current element. This getter should be used only on elements that contain a directive which starts a new isolate + * scope. Calling `scope()` on this element always returns the original non-isolate scope. + * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled. + * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top + * parent element is reached. + * + * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery. + * @returns {Object} jQuery object. + */ + +JQLite.expando = 'ng339'; + +var jqCache = JQLite.cache = {}, + jqId = 1, + addEventListenerFn = function(element, type, fn) { + element.addEventListener(type, fn, false); + }, + removeEventListenerFn = function(element, type, fn) { + element.removeEventListener(type, fn, false); + }; + +/* + * !!! This is an undocumented "private" function !!! + */ +JQLite._data = function(node) { + //jQuery always returns an object on cache miss + return this.cache[node[this.expando]] || {}; +}; + +function jqNextId() { return ++jqId; } + + +var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; +var MOZ_HACK_REGEXP = /^moz([A-Z])/; +var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"}; +var jqLiteMinErr = minErr('jqLite'); + +/** + * Converts snake_case to camelCase. + * Also there is special case for Moz prefix starting with upper case letter. + * @param name Name to normalize + */ +function camelCase(name) { + return name. + replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { + return offset ? letter.toUpperCase() : letter; + }). + replace(MOZ_HACK_REGEXP, 'Moz$1'); +} + +var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/; +var HTML_REGEXP = /<|&#?\w+;/; +var TAG_NAME_REGEXP = /<([\w:-]+)/; +var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi; + +var wrapMap = { + 'option': [1, ''], + + 'thead': [1, '', '
    '], + 'col': [2, '', '
    '], + 'tr': [2, '', '
    '], + 'td': [3, '', '
    '], + '_default': [0, "", ""] +}; + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + + +function jqLiteIsTextNode(html) { + return !HTML_REGEXP.test(html); +} + +function jqLiteAcceptsData(node) { + // The window object can accept data but has no nodeType + // Otherwise we are only interested in elements (1) and documents (9) + var nodeType = node.nodeType; + return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT; +} + +function jqLiteHasData(node) { + for (var key in jqCache[node.ng339]) { + return true; + } + return false; +} + +function jqLiteCleanData(nodes) { + for (var i = 0, ii = nodes.length; i < ii; i++) { + jqLiteRemoveData(nodes[i]); + } +} + +function jqLiteBuildFragment(html, context) { + var tmp, tag, wrap, + fragment = context.createDocumentFragment(), + nodes = [], i; + + if (jqLiteIsTextNode(html)) { + // Convert non-html into a text node + nodes.push(context.createTextNode(html)); + } else { + // Convert html into DOM nodes + tmp = tmp || fragment.appendChild(context.createElement("div")); + tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase(); + wrap = wrapMap[tag] || wrapMap._default; + tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1>") + wrap[2]; + + // Descend through wrappers to the right content + i = wrap[0]; + while (i--) { + tmp = tmp.lastChild; + } + + nodes = concat(nodes, tmp.childNodes); + + tmp = fragment.firstChild; + tmp.textContent = ""; + } + + // Remove wrapper from fragment + fragment.textContent = ""; + fragment.innerHTML = ""; // Clear inner HTML + forEach(nodes, function(node) { + fragment.appendChild(node); + }); + + return fragment; +} + +function jqLiteParseHTML(html, context) { + context = context || document; + var parsed; + + if ((parsed = SINGLE_TAG_REGEXP.exec(html))) { + return [context.createElement(parsed[1])]; + } + + if ((parsed = jqLiteBuildFragment(html, context))) { + return parsed.childNodes; + } + + return []; +} + +function jqLiteWrapNode(node, wrapper) { + var parent = node.parentNode; + + if (parent) { + parent.replaceChild(wrapper, node); + } + + wrapper.appendChild(node); +} + + +// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259. +var jqLiteContains = Node.prototype.contains || function(arg) { + // jshint bitwise: false + return !!(this.compareDocumentPosition(arg) & 16); + // jshint bitwise: true +}; + +///////////////////////////////////////////// +function JQLite(element) { + if (element instanceof JQLite) { + return element; + } + + var argIsString; + + if (isString(element)) { + element = trim(element); + argIsString = true; + } + if (!(this instanceof JQLite)) { + if (argIsString && element.charAt(0) != '<') { + throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element'); + } + return new JQLite(element); + } + + if (argIsString) { + jqLiteAddNodes(this, jqLiteParseHTML(element)); + } else { + jqLiteAddNodes(this, element); + } +} + +function jqLiteClone(element) { + return element.cloneNode(true); +} + +function jqLiteDealoc(element, onlyDescendants) { + if (!onlyDescendants) jqLiteRemoveData(element); + + if (element.querySelectorAll) { + var descendants = element.querySelectorAll('*'); + for (var i = 0, l = descendants.length; i < l; i++) { + jqLiteRemoveData(descendants[i]); + } + } +} + +function jqLiteOff(element, type, fn, unsupported) { + if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument'); + + var expandoStore = jqLiteExpandoStore(element); + var events = expandoStore && expandoStore.events; + var handle = expandoStore && expandoStore.handle; + + if (!handle) return; //no listeners registered + + if (!type) { + for (type in events) { + if (type !== '$destroy') { + removeEventListenerFn(element, type, handle); + } + delete events[type]; + } + } else { + + var removeHandler = function(type) { + var listenerFns = events[type]; + if (isDefined(fn)) { + arrayRemove(listenerFns || [], fn); + } + if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) { + removeEventListenerFn(element, type, handle); + delete events[type]; + } + }; + + forEach(type.split(' '), function(type) { + removeHandler(type); + if (MOUSE_EVENT_MAP[type]) { + removeHandler(MOUSE_EVENT_MAP[type]); + } + }); + } +} + +function jqLiteRemoveData(element, name) { + var expandoId = element.ng339; + var expandoStore = expandoId && jqCache[expandoId]; + + if (expandoStore) { + if (name) { + delete expandoStore.data[name]; + return; + } + + if (expandoStore.handle) { + if (expandoStore.events.$destroy) { + expandoStore.handle({}, '$destroy'); + } + jqLiteOff(element); + } + delete jqCache[expandoId]; + element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it + } +} + + +function jqLiteExpandoStore(element, createIfNecessary) { + var expandoId = element.ng339, + expandoStore = expandoId && jqCache[expandoId]; + + if (createIfNecessary && !expandoStore) { + element.ng339 = expandoId = jqNextId(); + expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined}; + } + + return expandoStore; +} + + +function jqLiteData(element, key, value) { + if (jqLiteAcceptsData(element)) { + + var isSimpleSetter = isDefined(value); + var isSimpleGetter = !isSimpleSetter && key && !isObject(key); + var massGetter = !key; + var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter); + var data = expandoStore && expandoStore.data; + + if (isSimpleSetter) { // data('key', value) + data[key] = value; + } else { + if (massGetter) { // data() + return data; + } else { + if (isSimpleGetter) { // data('key') + // don't force creation of expandoStore if it doesn't exist yet + return data && data[key]; + } else { // mass-setter: data({key1: val1, key2: val2}) + extend(data, key); + } + } + } + } +} + +function jqLiteHasClass(element, selector) { + if (!element.getAttribute) return false; + return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " "). + indexOf(" " + selector + " ") > -1); +} + +function jqLiteRemoveClass(element, cssClasses) { + if (cssClasses && element.setAttribute) { + forEach(cssClasses.split(' '), function(cssClass) { + element.setAttribute('class', trim( + (" " + (element.getAttribute('class') || '') + " ") + .replace(/[\n\t]/g, " ") + .replace(" " + trim(cssClass) + " ", " ")) + ); + }); + } +} + +function jqLiteAddClass(element, cssClasses) { + if (cssClasses && element.setAttribute) { + var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ') + .replace(/[\n\t]/g, " "); + + forEach(cssClasses.split(' '), function(cssClass) { + cssClass = trim(cssClass); + if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) { + existingClasses += cssClass + ' '; + } + }); + + element.setAttribute('class', trim(existingClasses)); + } +} + + +function jqLiteAddNodes(root, elements) { + // THIS CODE IS VERY HOT. Don't make changes without benchmarking. + + if (elements) { + + // if a Node (the most common case) + if (elements.nodeType) { + root[root.length++] = elements; + } else { + var length = elements.length; + + // if an Array or NodeList and not a Window + if (typeof length === 'number' && elements.window !== elements) { + if (length) { + for (var i = 0; i < length; i++) { + root[root.length++] = elements[i]; + } + } + } else { + root[root.length++] = elements; + } + } + } +} + + +function jqLiteController(element, name) { + return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller'); +} + +function jqLiteInheritedData(element, name, value) { + // if element is the document object work with the html element instead + // this makes $(document).scope() possible + if (element.nodeType == NODE_TYPE_DOCUMENT) { + element = element.documentElement; + } + var names = isArray(name) ? name : [name]; + + while (element) { + for (var i = 0, ii = names.length; i < ii; i++) { + if (isDefined(value = jqLite.data(element, names[i]))) return value; + } + + // If dealing with a document fragment node with a host element, and no parent, use the host + // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM + // to lookup parent controllers. + element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host); + } +} + +function jqLiteEmpty(element) { + jqLiteDealoc(element, true); + while (element.firstChild) { + element.removeChild(element.firstChild); + } +} + +function jqLiteRemove(element, keepData) { + if (!keepData) jqLiteDealoc(element); + var parent = element.parentNode; + if (parent) parent.removeChild(element); +} + + +function jqLiteDocumentLoaded(action, win) { + win = win || window; + if (win.document.readyState === 'complete') { + // Force the action to be run async for consistent behavior + // from the action's point of view + // i.e. it will definitely not be in a $apply + win.setTimeout(action); + } else { + // No need to unbind this handler as load is only ever called once + jqLite(win).on('load', action); + } +} + +////////////////////////////////////////// +// Functions which are declared directly. +////////////////////////////////////////// +var JQLitePrototype = JQLite.prototype = { + ready: function(fn) { + var fired = false; + + function trigger() { + if (fired) return; + fired = true; + fn(); + } + + // check if document is already loaded + if (document.readyState === 'complete') { + setTimeout(trigger); + } else { + this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9 + // we can not use jqLite since we are not done loading and jQuery could be loaded later. + // jshint -W064 + JQLite(window).on('load', trigger); // fallback to window.onload for others + // jshint +W064 + } + }, + toString: function() { + var value = []; + forEach(this, function(e) { value.push('' + e);}); + return '[' + value.join(', ') + ']'; + }, + + eq: function(index) { + return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]); + }, + + length: 0, + push: push, + sort: [].sort, + splice: [].splice +}; + +////////////////////////////////////////// +// Functions iterating getter/setters. +// these functions return self on setter and +// value on get. +////////////////////////////////////////// +var BOOLEAN_ATTR = {}; +forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) { + BOOLEAN_ATTR[lowercase(value)] = value; +}); +var BOOLEAN_ELEMENTS = {}; +forEach('input,select,option,textarea,button,form,details'.split(','), function(value) { + BOOLEAN_ELEMENTS[value] = true; +}); +var ALIASED_ATTR = { + 'ngMinlength': 'minlength', + 'ngMaxlength': 'maxlength', + 'ngMin': 'min', + 'ngMax': 'max', + 'ngPattern': 'pattern' +}; + +function getBooleanAttrName(element, name) { + // check dom last since we will most likely fail on name + var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()]; + + // booleanAttr is here twice to minimize DOM access + return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr; +} + +function getAliasedAttrName(name) { + return ALIASED_ATTR[name]; +} + +forEach({ + data: jqLiteData, + removeData: jqLiteRemoveData, + hasData: jqLiteHasData, + cleanData: jqLiteCleanData +}, function(fn, name) { + JQLite[name] = fn; +}); + +forEach({ + data: jqLiteData, + inheritedData: jqLiteInheritedData, + + scope: function(element) { + // Can't use jqLiteData here directly so we stay compatible with jQuery! + return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']); + }, + + isolateScope: function(element) { + // Can't use jqLiteData here directly so we stay compatible with jQuery! + return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate'); + }, + + controller: jqLiteController, + + injector: function(element) { + return jqLiteInheritedData(element, '$injector'); + }, + + removeAttr: function(element, name) { + element.removeAttribute(name); + }, + + hasClass: jqLiteHasClass, + + css: function(element, name, value) { + name = camelCase(name); + + if (isDefined(value)) { + element.style[name] = value; + } else { + return element.style[name]; + } + }, + + attr: function(element, name, value) { + var nodeType = element.nodeType; + if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) { + return; + } + var lowercasedName = lowercase(name); + if (BOOLEAN_ATTR[lowercasedName]) { + if (isDefined(value)) { + if (!!value) { + element[name] = true; + element.setAttribute(name, lowercasedName); + } else { + element[name] = false; + element.removeAttribute(lowercasedName); + } + } else { + return (element[name] || + (element.attributes.getNamedItem(name) || noop).specified) + ? lowercasedName + : undefined; + } + } else if (isDefined(value)) { + element.setAttribute(name, value); + } else if (element.getAttribute) { + // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code + // some elements (e.g. Document) don't have get attribute, so return undefined + var ret = element.getAttribute(name, 2); + // normalize non-existing attributes to undefined (as jQuery) + return ret === null ? undefined : ret; + } + }, + + prop: function(element, name, value) { + if (isDefined(value)) { + element[name] = value; + } else { + return element[name]; + } + }, + + text: (function() { + getText.$dv = ''; + return getText; + + function getText(element, value) { + if (isUndefined(value)) { + var nodeType = element.nodeType; + return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : ''; + } + element.textContent = value; + } + })(), + + val: function(element, value) { + if (isUndefined(value)) { + if (element.multiple && nodeName_(element) === 'select') { + var result = []; + forEach(element.options, function(option) { + if (option.selected) { + result.push(option.value || option.text); + } + }); + return result.length === 0 ? null : result; + } + return element.value; + } + element.value = value; + }, + + html: function(element, value) { + if (isUndefined(value)) { + return element.innerHTML; + } + jqLiteDealoc(element, true); + element.innerHTML = value; + }, + + empty: jqLiteEmpty +}, function(fn, name) { + /** + * Properties: writes return selection, reads return first value + */ + JQLite.prototype[name] = function(arg1, arg2) { + var i, key; + var nodeCount = this.length; + + // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it + // in a way that survives minification. + // jqLiteEmpty takes no arguments but is a setter. + if (fn !== jqLiteEmpty && + (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) { + if (isObject(arg1)) { + + // we are a write, but the object properties are the key/values + for (i = 0; i < nodeCount; i++) { + if (fn === jqLiteData) { + // data() takes the whole object in jQuery + fn(this[i], arg1); + } else { + for (key in arg1) { + fn(this[i], key, arg1[key]); + } + } + } + // return self for chaining + return this; + } else { + // we are a read, so read the first child. + // TODO: do we still need this? + var value = fn.$dv; + // Only if we have $dv do we iterate over all, otherwise it is just the first element. + var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount; + for (var j = 0; j < jj; j++) { + var nodeValue = fn(this[j], arg1, arg2); + value = value ? value + nodeValue : nodeValue; + } + return value; + } + } else { + // we are a write, so apply to all children + for (i = 0; i < nodeCount; i++) { + fn(this[i], arg1, arg2); + } + // return self for chaining + return this; + } + }; +}); + +function createEventHandler(element, events) { + var eventHandler = function(event, type) { + // jQuery specific api + event.isDefaultPrevented = function() { + return event.defaultPrevented; + }; + + var eventFns = events[type || event.type]; + var eventFnsLength = eventFns ? eventFns.length : 0; + + if (!eventFnsLength) return; + + if (isUndefined(event.immediatePropagationStopped)) { + var originalStopImmediatePropagation = event.stopImmediatePropagation; + event.stopImmediatePropagation = function() { + event.immediatePropagationStopped = true; + + if (event.stopPropagation) { + event.stopPropagation(); + } + + if (originalStopImmediatePropagation) { + originalStopImmediatePropagation.call(event); + } + }; + } + + event.isImmediatePropagationStopped = function() { + return event.immediatePropagationStopped === true; + }; + + // Some events have special handlers that wrap the real handler + var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper; + + // Copy event handlers in case event handlers array is modified during execution. + if ((eventFnsLength > 1)) { + eventFns = shallowCopy(eventFns); + } + + for (var i = 0; i < eventFnsLength; i++) { + if (!event.isImmediatePropagationStopped()) { + handlerWrapper(element, event, eventFns[i]); + } + } + }; + + // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all + // events on `element` + eventHandler.elem = element; + return eventHandler; +} + +function defaultHandlerWrapper(element, event, handler) { + handler.call(element, event); +} + +function specialMouseHandlerWrapper(target, event, handler) { + // Refer to jQuery's implementation of mouseenter & mouseleave + // Read about mouseenter and mouseleave: + // http://www.quirksmode.org/js/events_mouse.html#link8 + var related = event.relatedTarget; + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if (!related || (related !== target && !jqLiteContains.call(target, related))) { + handler.call(target, event); + } +} + +////////////////////////////////////////// +// Functions iterating traversal. +// These functions chain results into a single +// selector. +////////////////////////////////////////// +forEach({ + removeData: jqLiteRemoveData, + + on: function jqLiteOn(element, type, fn, unsupported) { + if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters'); + + // Do not add event handlers to non-elements because they will not be cleaned up. + if (!jqLiteAcceptsData(element)) { + return; + } + + var expandoStore = jqLiteExpandoStore(element, true); + var events = expandoStore.events; + var handle = expandoStore.handle; + + if (!handle) { + handle = expandoStore.handle = createEventHandler(element, events); + } + + // http://jsperf.com/string-indexof-vs-split + var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type]; + var i = types.length; + + var addHandler = function(type, specialHandlerWrapper, noEventListener) { + var eventFns = events[type]; + + if (!eventFns) { + eventFns = events[type] = []; + eventFns.specialHandlerWrapper = specialHandlerWrapper; + if (type !== '$destroy' && !noEventListener) { + addEventListenerFn(element, type, handle); + } + } + + eventFns.push(fn); + }; + + while (i--) { + type = types[i]; + if (MOUSE_EVENT_MAP[type]) { + addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper); + addHandler(type, undefined, true); + } else { + addHandler(type); + } + } + }, + + off: jqLiteOff, + + one: function(element, type, fn) { + element = jqLite(element); + + //add the listener twice so that when it is called + //you can remove the original function and still be + //able to call element.off(ev, fn) normally + element.on(type, function onFn() { + element.off(type, fn); + element.off(type, onFn); + }); + element.on(type, fn); + }, + + replaceWith: function(element, replaceNode) { + var index, parent = element.parentNode; + jqLiteDealoc(element); + forEach(new JQLite(replaceNode), function(node) { + if (index) { + parent.insertBefore(node, index.nextSibling); + } else { + parent.replaceChild(node, element); + } + index = node; + }); + }, + + children: function(element) { + var children = []; + forEach(element.childNodes, function(element) { + if (element.nodeType === NODE_TYPE_ELEMENT) { + children.push(element); + } + }); + return children; + }, + + contents: function(element) { + return element.contentDocument || element.childNodes || []; + }, + + append: function(element, node) { + var nodeType = element.nodeType; + if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return; + + node = new JQLite(node); + + for (var i = 0, ii = node.length; i < ii; i++) { + var child = node[i]; + element.appendChild(child); + } + }, + + prepend: function(element, node) { + if (element.nodeType === NODE_TYPE_ELEMENT) { + var index = element.firstChild; + forEach(new JQLite(node), function(child) { + element.insertBefore(child, index); + }); + } + }, + + wrap: function(element, wrapNode) { + jqLiteWrapNode(element, jqLite(wrapNode).eq(0).clone()[0]); + }, + + remove: jqLiteRemove, + + detach: function(element) { + jqLiteRemove(element, true); + }, + + after: function(element, newElement) { + var index = element, parent = element.parentNode; + newElement = new JQLite(newElement); + + for (var i = 0, ii = newElement.length; i < ii; i++) { + var node = newElement[i]; + parent.insertBefore(node, index.nextSibling); + index = node; + } + }, + + addClass: jqLiteAddClass, + removeClass: jqLiteRemoveClass, + + toggleClass: function(element, selector, condition) { + if (selector) { + forEach(selector.split(' '), function(className) { + var classCondition = condition; + if (isUndefined(classCondition)) { + classCondition = !jqLiteHasClass(element, className); + } + (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className); + }); + } + }, + + parent: function(element) { + var parent = element.parentNode; + return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null; + }, + + next: function(element) { + return element.nextElementSibling; + }, + + find: function(element, selector) { + if (element.getElementsByTagName) { + return element.getElementsByTagName(selector); + } else { + return []; + } + }, + + clone: jqLiteClone, + + triggerHandler: function(element, event, extraParameters) { + + var dummyEvent, eventFnsCopy, handlerArgs; + var eventName = event.type || event; + var expandoStore = jqLiteExpandoStore(element); + var events = expandoStore && expandoStore.events; + var eventFns = events && events[eventName]; + + if (eventFns) { + // Create a dummy event to pass to the handlers + dummyEvent = { + preventDefault: function() { this.defaultPrevented = true; }, + isDefaultPrevented: function() { return this.defaultPrevented === true; }, + stopImmediatePropagation: function() { this.immediatePropagationStopped = true; }, + isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; }, + stopPropagation: noop, + type: eventName, + target: element + }; + + // If a custom event was provided then extend our dummy event with it + if (event.type) { + dummyEvent = extend(dummyEvent, event); + } + + // Copy event handlers in case event handlers array is modified during execution. + eventFnsCopy = shallowCopy(eventFns); + handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent]; + + forEach(eventFnsCopy, function(fn) { + if (!dummyEvent.isImmediatePropagationStopped()) { + fn.apply(element, handlerArgs); + } + }); + } + } +}, function(fn, name) { + /** + * chaining functions + */ + JQLite.prototype[name] = function(arg1, arg2, arg3) { + var value; + + for (var i = 0, ii = this.length; i < ii; i++) { + if (isUndefined(value)) { + value = fn(this[i], arg1, arg2, arg3); + if (isDefined(value)) { + // any function which returns a value needs to be wrapped + value = jqLite(value); + } + } else { + jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3)); + } + } + return isDefined(value) ? value : this; + }; + + // bind legacy bind/unbind to on/off + JQLite.prototype.bind = JQLite.prototype.on; + JQLite.prototype.unbind = JQLite.prototype.off; +}); + + +// Provider for private $$jqLite service +function $$jqLiteProvider() { + this.$get = function $$jqLite() { + return extend(JQLite, { + hasClass: function(node, classes) { + if (node.attr) node = node[0]; + return jqLiteHasClass(node, classes); + }, + addClass: function(node, classes) { + if (node.attr) node = node[0]; + return jqLiteAddClass(node, classes); + }, + removeClass: function(node, classes) { + if (node.attr) node = node[0]; + return jqLiteRemoveClass(node, classes); + } + }); + }; +} + +/** + * Computes a hash of an 'obj'. + * Hash of a: + * string is string + * number is number as string + * object is either result of calling $$hashKey function on the object or uniquely generated id, + * that is also assigned to the $$hashKey property of the object. + * + * @param obj + * @returns {string} hash string such that the same input will have the same hash string. + * The resulting string key is in 'type:hashKey' format. + */ +function hashKey(obj, nextUidFn) { + var key = obj && obj.$$hashKey; + + if (key) { + if (typeof key === 'function') { + key = obj.$$hashKey(); + } + return key; + } + + var objType = typeof obj; + if (objType == 'function' || (objType == 'object' && obj !== null)) { + key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)(); + } else { + key = objType + ':' + obj; + } + + return key; +} + +/** + * HashMap which can use objects as keys + */ +function HashMap(array, isolatedUid) { + if (isolatedUid) { + var uid = 0; + this.nextUid = function() { + return ++uid; + }; + } + forEach(array, this.put, this); +} +HashMap.prototype = { + /** + * Store key value pair + * @param key key to store can be any type + * @param value value to store can be any type + */ + put: function(key, value) { + this[hashKey(key, this.nextUid)] = value; + }, + + /** + * @param key + * @returns {Object} the value for the key + */ + get: function(key) { + return this[hashKey(key, this.nextUid)]; + }, + + /** + * Remove the key/value pair + * @param key + */ + remove: function(key) { + var value = this[key = hashKey(key, this.nextUid)]; + delete this[key]; + return value; + } +}; + +var $$HashMapProvider = [function() { + this.$get = [function() { + return HashMap; + }]; +}]; + +/** + * @ngdoc function + * @module ng + * @name angular.injector + * @kind function + * + * @description + * Creates an injector object that can be used for retrieving services as well as for + * dependency injection (see {@link guide/di dependency injection}). + * + * @param {Array.} modules A list of module functions or their aliases. See + * {@link angular.module}. The `ng` module must be explicitly added. + * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which + * disallows argument name annotation inference. + * @returns {injector} Injector object. See {@link auto.$injector $injector}. + * + * @example + * Typical usage + * ```js + * // create an injector + * var $injector = angular.injector(['ng']); + * + * // use the injector to kick off your application + * // use the type inference to auto inject arguments, or use implicit injection + * $injector.invoke(function($rootScope, $compile, $document) { + * $compile($document)($rootScope); + * $rootScope.$digest(); + * }); + * ``` + * + * Sometimes you want to get access to the injector of a currently running Angular app + * from outside Angular. Perhaps, you want to inject and compile some markup after the + * application has been bootstrapped. You can do this using the extra `injector()` added + * to JQuery/jqLite elements. See {@link angular.element}. + * + * *This is fairly rare but could be the case if a third party library is injecting the + * markup.* + * + * In the following example a new block of HTML containing a `ng-controller` + * directive is added to the end of the document body by JQuery. We then compile and link + * it into the current AngularJS scope. + * + * ```js + * var $div = $('
    {{content.label}}
    '); + * $(document.body).append($div); + * + * angular.element(document).injector().invoke(function($compile) { + * var scope = angular.element($div).scope(); + * $compile($div)(scope); + * }); + * ``` + */ + + +/** + * @ngdoc module + * @name auto + * @description + * + * Implicit module which gets automatically added to each {@link auto.$injector $injector}. + */ + +var ARROW_ARG = /^([^\(]+?)=>/; +var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m; +var FN_ARG_SPLIT = /,/; +var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; +var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; +var $injectorMinErr = minErr('$injector'); + +function extractArgs(fn) { + var fnText = fn.toString().replace(STRIP_COMMENTS, ''), + args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS); + return args; +} + +function anonFn(fn) { + // For anonymous functions, showing at the very least the function signature can help in + // debugging. + var args = extractArgs(fn); + if (args) { + return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')'; + } + return 'fn'; +} + +function annotate(fn, strictDi, name) { + var $inject, + argDecl, + last; + + if (typeof fn === 'function') { + if (!($inject = fn.$inject)) { + $inject = []; + if (fn.length) { + if (strictDi) { + if (!isString(name) || !name) { + name = fn.name || anonFn(fn); + } + throw $injectorMinErr('strictdi', + '{0} is not using explicit annotation and cannot be invoked in strict mode', name); + } + argDecl = extractArgs(fn); + forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) { + arg.replace(FN_ARG, function(all, underscore, name) { + $inject.push(name); + }); + }); + } + fn.$inject = $inject; + } + } else if (isArray(fn)) { + last = fn.length - 1; + assertArgFn(fn[last], 'fn'); + $inject = fn.slice(0, last); + } else { + assertArgFn(fn, 'fn', true); + } + return $inject; +} + +/////////////////////////////////////// + +/** + * @ngdoc service + * @name $injector + * + * @description + * + * `$injector` is used to retrieve object instances as defined by + * {@link auto.$provide provider}, instantiate types, invoke methods, + * and load modules. + * + * The following always holds true: + * + * ```js + * var $injector = angular.injector(); + * expect($injector.get('$injector')).toBe($injector); + * expect($injector.invoke(function($injector) { + * return $injector; + * })).toBe($injector); + * ``` + * + * # Injection Function Annotation + * + * JavaScript does not have annotations, and annotations are needed for dependency injection. The + * following are all valid ways of annotating function with injection arguments and are equivalent. + * + * ```js + * // inferred (only works if code not minified/obfuscated) + * $injector.invoke(function(serviceA){}); + * + * // annotated + * function explicit(serviceA) {}; + * explicit.$inject = ['serviceA']; + * $injector.invoke(explicit); + * + * // inline + * $injector.invoke(['serviceA', function(serviceA){}]); + * ``` + * + * ## Inference + * + * In JavaScript calling `toString()` on a function returns the function definition. The definition + * can then be parsed and the function arguments can be extracted. This method of discovering + * annotations is disallowed when the injector is in strict mode. + * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the + * argument names. + * + * ## `$inject` Annotation + * By adding an `$inject` property onto a function the injection parameters can be specified. + * + * ## Inline + * As an array of injection names, where the last item in the array is the function to call. + */ + +/** + * @ngdoc method + * @name $injector#get + * + * @description + * Return an instance of the service. + * + * @param {string} name The name of the instance to retrieve. + * @param {string=} caller An optional string to provide the origin of the function call for error messages. + * @return {*} The instance. + */ + +/** + * @ngdoc method + * @name $injector#invoke + * + * @description + * Invoke the method and supply the method arguments from the `$injector`. + * + * @param {Function|Array.} fn The injectable function to invoke. Function parameters are + * injected according to the {@link guide/di $inject Annotation} rules. + * @param {Object=} self The `this` for the invoked method. + * @param {Object=} locals Optional object. If preset then any argument names are read from this + * object first, before the `$injector` is consulted. + * @returns {*} the value returned by the invoked `fn` function. + */ + +/** + * @ngdoc method + * @name $injector#has + * + * @description + * Allows the user to query if the particular service exists. + * + * @param {string} name Name of the service to query. + * @returns {boolean} `true` if injector has given service. + */ + +/** + * @ngdoc method + * @name $injector#instantiate + * @description + * Create a new instance of JS type. The method takes a constructor function, invokes the new + * operator, and supplies all of the arguments to the constructor function as specified by the + * constructor annotation. + * + * @param {Function} Type Annotated constructor function. + * @param {Object=} locals Optional object. If preset then any argument names are read from this + * object first, before the `$injector` is consulted. + * @returns {Object} new instance of `Type`. + */ + +/** + * @ngdoc method + * @name $injector#annotate + * + * @description + * Returns an array of service names which the function is requesting for injection. This API is + * used by the injector to determine which services need to be injected into the function when the + * function is invoked. There are three ways in which the function can be annotated with the needed + * dependencies. + * + * # Argument names + * + * The simplest form is to extract the dependencies from the arguments of the function. This is done + * by converting the function into a string using `toString()` method and extracting the argument + * names. + * ```js + * // Given + * function MyController($scope, $route) { + * // ... + * } + * + * // Then + * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); + * ``` + * + * You can disallow this method by using strict injection mode. + * + * This method does not work with code minification / obfuscation. For this reason the following + * annotation strategies are supported. + * + * # The `$inject` property + * + * If a function has an `$inject` property and its value is an array of strings, then the strings + * represent names of services to be injected into the function. + * ```js + * // Given + * var MyController = function(obfuscatedScope, obfuscatedRoute) { + * // ... + * } + * // Define function dependencies + * MyController['$inject'] = ['$scope', '$route']; + * + * // Then + * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); + * ``` + * + * # The array notation + * + * It is often desirable to inline Injected functions and that's when setting the `$inject` property + * is very inconvenient. In these situations using the array notation to specify the dependencies in + * a way that survives minification is a better choice: + * + * ```js + * // We wish to write this (not minification / obfuscation safe) + * injector.invoke(function($compile, $rootScope) { + * // ... + * }); + * + * // We are forced to write break inlining + * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) { + * // ... + * }; + * tmpFn.$inject = ['$compile', '$rootScope']; + * injector.invoke(tmpFn); + * + * // To better support inline function the inline annotation is supported + * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) { + * // ... + * }]); + * + * // Therefore + * expect(injector.annotate( + * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}]) + * ).toEqual(['$compile', '$rootScope']); + * ``` + * + * @param {Function|Array.} fn Function for which dependent service names need to + * be retrieved as described above. + * + * @param {boolean=} [strictDi=false] Disallow argument name annotation inference. + * + * @returns {Array.} The names of the services which the function requires. + */ + + + + +/** + * @ngdoc service + * @name $provide + * + * @description + * + * The {@link auto.$provide $provide} service has a number of methods for registering components + * with the {@link auto.$injector $injector}. Many of these functions are also exposed on + * {@link angular.Module}. + * + * An Angular **service** is a singleton object created by a **service factory**. These **service + * factories** are functions which, in turn, are created by a **service provider**. + * The **service providers** are constructor functions. When instantiated they must contain a + * property called `$get`, which holds the **service factory** function. + * + * When you request a service, the {@link auto.$injector $injector} is responsible for finding the + * correct **service provider**, instantiating it and then calling its `$get` **service factory** + * function to get the instance of the **service**. + * + * Often services have no configuration options and there is no need to add methods to the service + * provider. The provider will be no more than a constructor function with a `$get` property. For + * these cases the {@link auto.$provide $provide} service has additional helper methods to register + * services without specifying a provider. + * + * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the + * {@link auto.$injector $injector} + * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by + * providers and services. + * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by + * services, not providers. + * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`, + * that will be wrapped in a **service provider** object, whose `$get` property will contain the + * given factory function. + * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class` + * that will be wrapped in a **service provider** object, whose `$get` property will instantiate + * a new object using the given constructor function. + * + * See the individual methods for more information and examples. + */ + +/** + * @ngdoc method + * @name $provide#provider + * @description + * + * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions + * are constructor functions, whose instances are responsible for "providing" a factory for a + * service. + * + * Service provider names start with the name of the service they provide followed by `Provider`. + * For example, the {@link ng.$log $log} service has a provider called + * {@link ng.$logProvider $logProvider}. + * + * Service provider objects can have additional methods which allow configuration of the provider + * and its service. Importantly, you can configure what kind of service is created by the `$get` + * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a + * method {@link ng.$logProvider#debugEnabled debugEnabled} + * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the + * console or not. + * + * @param {string} name The name of the instance. NOTE: the provider will be available under `name + + 'Provider'` key. + * @param {(Object|function())} provider If the provider is: + * + * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using + * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created. + * - `Constructor`: a new instance of the provider will be created using + * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`. + * + * @returns {Object} registered provider instance + + * @example + * + * The following example shows how to create a simple event tracking service and register it using + * {@link auto.$provide#provider $provide.provider()}. + * + * ```js + * // Define the eventTracker provider + * function EventTrackerProvider() { + * var trackingUrl = '/track'; + * + * // A provider method for configuring where the tracked events should been saved + * this.setTrackingUrl = function(url) { + * trackingUrl = url; + * }; + * + * // The service factory function + * this.$get = ['$http', function($http) { + * var trackedEvents = {}; + * return { + * // Call this to track an event + * event: function(event) { + * var count = trackedEvents[event] || 0; + * count += 1; + * trackedEvents[event] = count; + * return count; + * }, + * // Call this to save the tracked events to the trackingUrl + * save: function() { + * $http.post(trackingUrl, trackedEvents); + * } + * }; + * }]; + * } + * + * describe('eventTracker', function() { + * var postSpy; + * + * beforeEach(module(function($provide) { + * // Register the eventTracker provider + * $provide.provider('eventTracker', EventTrackerProvider); + * })); + * + * beforeEach(module(function(eventTrackerProvider) { + * // Configure eventTracker provider + * eventTrackerProvider.setTrackingUrl('/custom-track'); + * })); + * + * it('tracks events', inject(function(eventTracker) { + * expect(eventTracker.event('login')).toEqual(1); + * expect(eventTracker.event('login')).toEqual(2); + * })); + * + * it('saves to the tracking url', inject(function(eventTracker, $http) { + * postSpy = spyOn($http, 'post'); + * eventTracker.event('login'); + * eventTracker.save(); + * expect(postSpy).toHaveBeenCalled(); + * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track'); + * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track'); + * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 }); + * })); + * }); + * ``` + */ + +/** + * @ngdoc method + * @name $provide#factory + * @description + * + * Register a **service factory**, which will be called to return the service instance. + * This is short for registering a service where its provider consists of only a `$get` property, + * which is the given service factory function. + * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to + * configure your service in a provider. + * + * @param {string} name The name of the instance. + * @param {Function|Array.} $getFn The injectable $getFn for the instance creation. + * Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`. + * @returns {Object} registered provider instance + * + * @example + * Here is an example of registering a service + * ```js + * $provide.factory('ping', ['$http', function($http) { + * return function ping() { + * return $http.send('/ping'); + * }; + * }]); + * ``` + * You would then inject and use this service like this: + * ```js + * someModule.controller('Ctrl', ['ping', function(ping) { + * ping(); + * }]); + * ``` + */ + + +/** + * @ngdoc method + * @name $provide#service + * @description + * + * Register a **service constructor**, which will be invoked with `new` to create the service + * instance. + * This is short for registering a service where its provider's `$get` property is a factory + * function that returns an instance instantiated by the injector from the service constructor + * function. + * + * Internally it looks a bit like this: + * + * ``` + * { + * $get: function() { + * return $injector.instantiate(constructor); + * } + * } + * ``` + * + * + * You should use {@link auto.$provide#service $provide.service(class)} if you define your service + * as a type/class. + * + * @param {string} name The name of the instance. + * @param {Function|Array.} constructor An injectable class (constructor function) + * that will be instantiated. + * @returns {Object} registered provider instance + * + * @example + * Here is an example of registering a service using + * {@link auto.$provide#service $provide.service(class)}. + * ```js + * var Ping = function($http) { + * this.$http = $http; + * }; + * + * Ping.$inject = ['$http']; + * + * Ping.prototype.send = function() { + * return this.$http.get('/ping'); + * }; + * $provide.service('ping', Ping); + * ``` + * You would then inject and use this service like this: + * ```js + * someModule.controller('Ctrl', ['ping', function(ping) { + * ping.send(); + * }]); + * ``` + */ + + +/** + * @ngdoc method + * @name $provide#value + * @description + * + * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a + * number, an array, an object or a function. This is short for registering a service where its + * provider's `$get` property is a factory function that takes no arguments and returns the **value + * service**. That also means it is not possible to inject other services into a value service. + * + * Value services are similar to constant services, except that they cannot be injected into a + * module configuration function (see {@link angular.Module#config}) but they can be overridden by + * an Angular {@link auto.$provide#decorator decorator}. + * + * @param {string} name The name of the instance. + * @param {*} value The value. + * @returns {Object} registered provider instance + * + * @example + * Here are some examples of creating value services. + * ```js + * $provide.value('ADMIN_USER', 'admin'); + * + * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 }); + * + * $provide.value('halfOf', function(value) { + * return value / 2; + * }); + * ``` + */ + + +/** + * @ngdoc method + * @name $provide#constant + * @description + * + * Register a **constant service** with the {@link auto.$injector $injector}, such as a string, + * a number, an array, an object or a function. Like the {@link auto.$provide#value value}, it is not + * possible to inject other services into a constant. + * + * But unlike {@link auto.$provide#value value}, a constant can be + * injected into a module configuration function (see {@link angular.Module#config}) and it cannot + * be overridden by an Angular {@link auto.$provide#decorator decorator}. + * + * @param {string} name The name of the constant. + * @param {*} value The constant value. + * @returns {Object} registered instance + * + * @example + * Here a some examples of creating constants: + * ```js + * $provide.constant('SHARD_HEIGHT', 306); + * + * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']); + * + * $provide.constant('double', function(value) { + * return value * 2; + * }); + * ``` + */ + + +/** + * @ngdoc method + * @name $provide#decorator + * @description + * + * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator + * intercepts the creation of a service, allowing it to override or modify the behavior of the + * service. The object returned by the decorator may be the original service, or a new service + * object which replaces or wraps and delegates to the original service. + * + * @param {string} name The name of the service to decorate. + * @param {Function|Array.} decorator This function will be invoked when the service needs to be + * instantiated and should return the decorated service instance. The function is called using + * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable. + * Local injection arguments: + * + * * `$delegate` - The original service instance, which can be monkey patched, configured, + * decorated or delegated to. + * + * @example + * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting + * calls to {@link ng.$log#error $log.warn()}. + * ```js + * $provide.decorator('$log', ['$delegate', function($delegate) { + * $delegate.warn = $delegate.error; + * return $delegate; + * }]); + * ``` + */ + + +function createInjector(modulesToLoad, strictDi) { + strictDi = (strictDi === true); + var INSTANTIATING = {}, + providerSuffix = 'Provider', + path = [], + loadedModules = new HashMap([], true), + providerCache = { + $provide: { + provider: supportObject(provider), + factory: supportObject(factory), + service: supportObject(service), + value: supportObject(value), + constant: supportObject(constant), + decorator: decorator + } + }, + providerInjector = (providerCache.$injector = + createInternalInjector(providerCache, function(serviceName, caller) { + if (angular.isString(caller)) { + path.push(caller); + } + throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- ')); + })), + instanceCache = {}, + protoInstanceInjector = + createInternalInjector(instanceCache, function(serviceName, caller) { + var provider = providerInjector.get(serviceName + providerSuffix, caller); + return instanceInjector.invoke( + provider.$get, provider, undefined, serviceName); + }), + instanceInjector = protoInstanceInjector; + + providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) }; + var runBlocks = loadModules(modulesToLoad); + instanceInjector = protoInstanceInjector.get('$injector'); + instanceInjector.strictDi = strictDi; + forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); }); + + return instanceInjector; + + //////////////////////////////////// + // $provider + //////////////////////////////////// + + function supportObject(delegate) { + return function(key, value) { + if (isObject(key)) { + forEach(key, reverseParams(delegate)); + } else { + return delegate(key, value); + } + }; + } + + function provider(name, provider_) { + assertNotHasOwnProperty(name, 'service'); + if (isFunction(provider_) || isArray(provider_)) { + provider_ = providerInjector.instantiate(provider_); + } + if (!provider_.$get) { + throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name); + } + return providerCache[name + providerSuffix] = provider_; + } + + function enforceReturnValue(name, factory) { + return function enforcedReturnValue() { + var result = instanceInjector.invoke(factory, this); + if (isUndefined(result)) { + throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name); + } + return result; + }; + } + + function factory(name, factoryFn, enforce) { + return provider(name, { + $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn + }); + } + + function service(name, constructor) { + return factory(name, ['$injector', function($injector) { + return $injector.instantiate(constructor); + }]); + } + + function value(name, val) { return factory(name, valueFn(val), false); } + + function constant(name, value) { + assertNotHasOwnProperty(name, 'constant'); + providerCache[name] = value; + instanceCache[name] = value; + } + + function decorator(serviceName, decorFn) { + var origProvider = providerInjector.get(serviceName + providerSuffix), + orig$get = origProvider.$get; + + origProvider.$get = function() { + var origInstance = instanceInjector.invoke(orig$get, origProvider); + return instanceInjector.invoke(decorFn, null, {$delegate: origInstance}); + }; + } + + //////////////////////////////////// + // Module Loading + //////////////////////////////////// + function loadModules(modulesToLoad) { + assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array'); + var runBlocks = [], moduleFn; + forEach(modulesToLoad, function(module) { + if (loadedModules.get(module)) return; + loadedModules.put(module, true); + + function runInvokeQueue(queue) { + var i, ii; + for (i = 0, ii = queue.length; i < ii; i++) { + var invokeArgs = queue[i], + provider = providerInjector.get(invokeArgs[0]); + + provider[invokeArgs[1]].apply(provider, invokeArgs[2]); + } + } + + try { + if (isString(module)) { + moduleFn = angularModule(module); + runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); + runInvokeQueue(moduleFn._invokeQueue); + runInvokeQueue(moduleFn._configBlocks); + } else if (isFunction(module)) { + runBlocks.push(providerInjector.invoke(module)); + } else if (isArray(module)) { + runBlocks.push(providerInjector.invoke(module)); + } else { + assertArgFn(module, 'module'); + } + } catch (e) { + if (isArray(module)) { + module = module[module.length - 1]; + } + if (e.message && e.stack && e.stack.indexOf(e.message) == -1) { + // Safari & FF's stack traces don't contain error.message content + // unlike those of Chrome and IE + // So if stack doesn't contain message, we create a new string that contains both. + // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here. + /* jshint -W022 */ + e = e.message + '\n' + e.stack; + } + throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}", + module, e.stack || e.message || e); + } + }); + return runBlocks; + } + + //////////////////////////////////// + // internal Injector + //////////////////////////////////// + + function createInternalInjector(cache, factory) { + + function getService(serviceName, caller) { + if (cache.hasOwnProperty(serviceName)) { + if (cache[serviceName] === INSTANTIATING) { + throw $injectorMinErr('cdep', 'Circular dependency found: {0}', + serviceName + ' <- ' + path.join(' <- ')); + } + return cache[serviceName]; + } else { + try { + path.unshift(serviceName); + cache[serviceName] = INSTANTIATING; + return cache[serviceName] = factory(serviceName, caller); + } catch (err) { + if (cache[serviceName] === INSTANTIATING) { + delete cache[serviceName]; + } + throw err; + } finally { + path.shift(); + } + } + } + + + function injectionArgs(fn, locals, serviceName) { + var args = [], + $inject = createInjector.$$annotate(fn, strictDi, serviceName); + + for (var i = 0, length = $inject.length; i < length; i++) { + var key = $inject[i]; + if (typeof key !== 'string') { + throw $injectorMinErr('itkn', + 'Incorrect injection token! Expected service name as string, got {0}', key); + } + args.push(locals && locals.hasOwnProperty(key) ? locals[key] : + getService(key, serviceName)); + } + return args; + } + + function isClass(func) { + // IE 9-11 do not support classes and IE9 leaks with the code below. + if (msie <= 11) { + return false; + } + // Workaround for MS Edge. + // Check https://connect.microsoft.com/IE/Feedback/Details/2211653 + return typeof func === 'function' + && /^(?:class\s|constructor\()/.test(Function.prototype.toString.call(func)); + } + + function invoke(fn, self, locals, serviceName) { + if (typeof locals === 'string') { + serviceName = locals; + locals = null; + } + + var args = injectionArgs(fn, locals, serviceName); + if (isArray(fn)) { + fn = fn[fn.length - 1]; + } + + if (!isClass(fn)) { + // http://jsperf.com/angularjs-invoke-apply-vs-switch + // #5388 + return fn.apply(self, args); + } else { + args.unshift(null); + return new (Function.prototype.bind.apply(fn, args))(); + } + } + + + function instantiate(Type, locals, serviceName) { + // Check if Type is annotated and use just the given function at n-1 as parameter + // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]); + var ctor = (isArray(Type) ? Type[Type.length - 1] : Type); + var args = injectionArgs(Type, locals, serviceName); + // Empty object at position 0 is ignored for invocation with `new`, but required. + args.unshift(null); + return new (Function.prototype.bind.apply(ctor, args))(); + } + + + return { + invoke: invoke, + instantiate: instantiate, + get: getService, + annotate: createInjector.$$annotate, + has: function(name) { + return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name); + } + }; + } +} + +createInjector.$$annotate = annotate; + +/** + * @ngdoc provider + * @name $anchorScrollProvider + * + * @description + * Use `$anchorScrollProvider` to disable automatic scrolling whenever + * {@link ng.$location#hash $location.hash()} changes. + */ +function $AnchorScrollProvider() { + + var autoScrollingEnabled = true; + + /** + * @ngdoc method + * @name $anchorScrollProvider#disableAutoScrolling + * + * @description + * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to + * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.
    + * Use this method to disable automatic scrolling. + * + * If automatic scrolling is disabled, one must explicitly call + * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the + * current hash. + */ + this.disableAutoScrolling = function() { + autoScrollingEnabled = false; + }; + + /** + * @ngdoc service + * @name $anchorScroll + * @kind function + * @requires $window + * @requires $location + * @requires $rootScope + * + * @description + * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the + * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified + * in the + * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document). + * + * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to + * match any anchor whenever it changes. This can be disabled by calling + * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}. + * + * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a + * vertical scroll-offset (either fixed or dynamic). + * + * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of + * {@link ng.$location#hash $location.hash()} will be used. + * + * @property {(number|function|jqLite)} yOffset + * If set, specifies a vertical scroll-offset. This is often useful when there are fixed + * positioned elements at the top of the page, such as navbars, headers etc. + * + * `yOffset` can be specified in various ways: + * - **number**: A fixed number of pixels to be used as offset.

    + * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return + * a number representing the offset (in pixels).

    + * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from + * the top of the page to the element's bottom will be used as offset.
    + * **Note**: The element will be taken into account only as long as its `position` is set to + * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust + * their height and/or positioning according to the viewport's size. + * + *
    + *
    + * In order for `yOffset` to work properly, scrolling should take place on the document's root and + * not some child element. + *
    + * + * @example + + +
    + Go to bottom + You're at the bottom! +
    +
    + + angular.module('anchorScrollExample', []) + .controller('ScrollController', ['$scope', '$location', '$anchorScroll', + function ($scope, $location, $anchorScroll) { + $scope.gotoBottom = function() { + // set the location.hash to the id of + // the element you wish to scroll to. + $location.hash('bottom'); + + // call $anchorScroll() + $anchorScroll(); + }; + }]); + + + #scrollArea { + height: 280px; + overflow: auto; + } + + #bottom { + display: block; + margin-top: 2000px; + } + +
    + * + *
    + * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value). + * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details. + * + * @example + + + +
    + Anchor {{x}} of 5 +
    +
    + + angular.module('anchorScrollOffsetExample', []) + .run(['$anchorScroll', function($anchorScroll) { + $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels + }]) + .controller('headerCtrl', ['$anchorScroll', '$location', '$scope', + function ($anchorScroll, $location, $scope) { + $scope.gotoAnchor = function(x) { + var newHash = 'anchor' + x; + if ($location.hash() !== newHash) { + // set the $location.hash to `newHash` and + // $anchorScroll will automatically scroll to it + $location.hash('anchor' + x); + } else { + // call $anchorScroll() explicitly, + // since $location.hash hasn't changed + $anchorScroll(); + } + }; + } + ]); + + + body { + padding-top: 50px; + } + + .anchor { + border: 2px dashed DarkOrchid; + padding: 10px 10px 200px 10px; + } + + .fixed-header { + background-color: rgba(0, 0, 0, 0.2); + height: 50px; + position: fixed; + top: 0; left: 0; right: 0; + } + + .fixed-header > a { + display: inline-block; + margin: 5px 15px; + } + +
    + */ + this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) { + var document = $window.document; + + // Helper function to get first anchor from a NodeList + // (using `Array#some()` instead of `angular#forEach()` since it's more performant + // and working in all supported browsers.) + function getFirstAnchor(list) { + var result = null; + Array.prototype.some.call(list, function(element) { + if (nodeName_(element) === 'a') { + result = element; + return true; + } + }); + return result; + } + + function getYOffset() { + + var offset = scroll.yOffset; + + if (isFunction(offset)) { + offset = offset(); + } else if (isElement(offset)) { + var elem = offset[0]; + var style = $window.getComputedStyle(elem); + if (style.position !== 'fixed') { + offset = 0; + } else { + offset = elem.getBoundingClientRect().bottom; + } + } else if (!isNumber(offset)) { + offset = 0; + } + + return offset; + } + + function scrollTo(elem) { + if (elem) { + elem.scrollIntoView(); + + var offset = getYOffset(); + + if (offset) { + // `offset` is the number of pixels we should scroll UP in order to align `elem` properly. + // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the + // top of the viewport. + // + // IF the number of pixels from the top of `elem` to the end of the page's content is less + // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some + // way down the page. + // + // This is often the case for elements near the bottom of the page. + // + // In such cases we do not need to scroll the whole `offset` up, just the difference between + // the top of the element and the offset, which is enough to align the top of `elem` at the + // desired position. + var elemTop = elem.getBoundingClientRect().top; + $window.scrollBy(0, elemTop - offset); + } + } else { + $window.scrollTo(0, 0); + } + } + + function scroll(hash) { + hash = isString(hash) ? hash : $location.hash(); + var elm; + + // empty hash, scroll to the top of the page + if (!hash) scrollTo(null); + + // element with given id + else if ((elm = document.getElementById(hash))) scrollTo(elm); + + // first anchor with given name :-D + else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm); + + // no element and hash == 'top', scroll to the top of the page + else if (hash === 'top') scrollTo(null); + } + + // does not scroll when user clicks on anchor link that is currently on + // (no url change, no $location.hash() change), browser native does scroll + if (autoScrollingEnabled) { + $rootScope.$watch(function autoScrollWatch() {return $location.hash();}, + function autoScrollWatchAction(newVal, oldVal) { + // skip the initial scroll if $location.hash is empty + if (newVal === oldVal && newVal === '') return; + + jqLiteDocumentLoaded(function() { + $rootScope.$evalAsync(scroll); + }); + }); + } + + return scroll; + }]; +} + +var $animateMinErr = minErr('$animate'); +var ELEMENT_NODE = 1; +var NG_ANIMATE_CLASSNAME = 'ng-animate'; + +function mergeClasses(a,b) { + if (!a && !b) return ''; + if (!a) return b; + if (!b) return a; + if (isArray(a)) a = a.join(' '); + if (isArray(b)) b = b.join(' '); + return a + ' ' + b; +} + +function extractElementNode(element) { + for (var i = 0; i < element.length; i++) { + var elm = element[i]; + if (elm.nodeType === ELEMENT_NODE) { + return elm; + } + } +} + +function splitClasses(classes) { + if (isString(classes)) { + classes = classes.split(' '); + } + + // Use createMap() to prevent class assumptions involving property names in + // Object.prototype + var obj = createMap(); + forEach(classes, function(klass) { + // sometimes the split leaves empty string values + // incase extra spaces were applied to the options + if (klass.length) { + obj[klass] = true; + } + }); + return obj; +} + +// if any other type of options value besides an Object value is +// passed into the $animate.method() animation then this helper code +// will be run which will ignore it. While this patch is not the +// greatest solution to this, a lot of existing plugins depend on +// $animate to either call the callback (< 1.2) or return a promise +// that can be changed. This helper function ensures that the options +// are wiped clean incase a callback function is provided. +function prepareAnimateOptions(options) { + return isObject(options) + ? options + : {}; +} + +var $$CoreAnimateJsProvider = function() { + this.$get = noop; +}; + +// this is prefixed with Core since it conflicts with +// the animateQueueProvider defined in ngAnimate/animateQueue.js +var $$CoreAnimateQueueProvider = function() { + var postDigestQueue = new HashMap(); + var postDigestElements = []; + + this.$get = ['$$AnimateRunner', '$rootScope', + function($$AnimateRunner, $rootScope) { + return { + enabled: noop, + on: noop, + off: noop, + pin: noop, + + push: function(element, event, options, domOperation) { + domOperation && domOperation(); + + options = options || {}; + options.from && element.css(options.from); + options.to && element.css(options.to); + + if (options.addClass || options.removeClass) { + addRemoveClassesPostDigest(element, options.addClass, options.removeClass); + } + + var runner = new $$AnimateRunner(); // jshint ignore:line + + // since there are no animations to run the runner needs to be + // notified that the animation call is complete. + runner.complete(); + return runner; + } + }; + + + function updateData(data, classes, value) { + var changed = false; + if (classes) { + classes = isString(classes) ? classes.split(' ') : + isArray(classes) ? classes : []; + forEach(classes, function(className) { + if (className) { + changed = true; + data[className] = value; + } + }); + } + return changed; + } + + function handleCSSClassChanges() { + forEach(postDigestElements, function(element) { + var data = postDigestQueue.get(element); + if (data) { + var existing = splitClasses(element.attr('class')); + var toAdd = ''; + var toRemove = ''; + forEach(data, function(status, className) { + var hasClass = !!existing[className]; + if (status !== hasClass) { + if (status) { + toAdd += (toAdd.length ? ' ' : '') + className; + } else { + toRemove += (toRemove.length ? ' ' : '') + className; + } + } + }); + + forEach(element, function(elm) { + toAdd && jqLiteAddClass(elm, toAdd); + toRemove && jqLiteRemoveClass(elm, toRemove); + }); + postDigestQueue.remove(element); + } + }); + postDigestElements.length = 0; + } + + + function addRemoveClassesPostDigest(element, add, remove) { + var data = postDigestQueue.get(element) || {}; + + var classesAdded = updateData(data, add, true); + var classesRemoved = updateData(data, remove, false); + + if (classesAdded || classesRemoved) { + + postDigestQueue.put(element, data); + postDigestElements.push(element); + + if (postDigestElements.length === 1) { + $rootScope.$$postDigest(handleCSSClassChanges); + } + } + } + }]; +}; + +/** + * @ngdoc provider + * @name $animateProvider + * + * @description + * Default implementation of $animate that doesn't perform any animations, instead just + * synchronously performs DOM updates and resolves the returned runner promise. + * + * In order to enable animations the `ngAnimate` module has to be loaded. + * + * To see the functional implementation check out `src/ngAnimate/animate.js`. + */ +var $AnimateProvider = ['$provide', function($provide) { + var provider = this; + + this.$$registeredAnimations = Object.create(null); + + /** + * @ngdoc method + * @name $animateProvider#register + * + * @description + * Registers a new injectable animation factory function. The factory function produces the + * animation object which contains callback functions for each event that is expected to be + * animated. + * + * * `eventFn`: `function(element, ... , doneFunction, options)` + * The element to animate, the `doneFunction` and the options fed into the animation. Depending + * on the type of animation additional arguments will be injected into the animation function. The + * list below explains the function signatures for the different animation methods: + * + * - setClass: function(element, addedClasses, removedClasses, doneFunction, options) + * - addClass: function(element, addedClasses, doneFunction, options) + * - removeClass: function(element, removedClasses, doneFunction, options) + * - enter, leave, move: function(element, doneFunction, options) + * - animate: function(element, fromStyles, toStyles, doneFunction, options) + * + * Make sure to trigger the `doneFunction` once the animation is fully complete. + * + * ```js + * return { + * //enter, leave, move signature + * eventFn : function(element, done, options) { + * //code to run the animation + * //once complete, then run done() + * return function endFunction(wasCancelled) { + * //code to cancel the animation + * } + * } + * } + * ``` + * + * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to). + * @param {Function} factory The factory function that will be executed to return the animation + * object. + */ + this.register = function(name, factory) { + if (name && name.charAt(0) !== '.') { + throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name); + } + + var key = name + '-animation'; + provider.$$registeredAnimations[name.substr(1)] = key; + $provide.factory(key, factory); + }; + + /** + * @ngdoc method + * @name $animateProvider#classNameFilter + * + * @description + * Sets and/or returns the CSS class regular expression that is checked when performing + * an animation. Upon bootstrap the classNameFilter value is not set at all and will + * therefore enable $animate to attempt to perform an animation on any element that is triggered. + * When setting the `classNameFilter` value, animations will only be performed on elements + * that successfully match the filter expression. This in turn can boost performance + * for low-powered devices as well as applications containing a lot of structural operations. + * @param {RegExp=} expression The className expression which will be checked against all animations + * @return {RegExp} The current CSS className expression value. If null then there is no expression value + */ + this.classNameFilter = function(expression) { + if (arguments.length === 1) { + this.$$classNameFilter = (expression instanceof RegExp) ? expression : null; + if (this.$$classNameFilter) { + var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)"); + if (reservedRegex.test(this.$$classNameFilter.toString())) { + throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME); + + } + } + } + return this.$$classNameFilter; + }; + + this.$get = ['$$animateQueue', function($$animateQueue) { + function domInsert(element, parentElement, afterElement) { + // if for some reason the previous element was removed + // from the dom sometime before this code runs then let's + // just stick to using the parent element as the anchor + if (afterElement) { + var afterNode = extractElementNode(afterElement); + if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) { + afterElement = null; + } + } + afterElement ? afterElement.after(element) : parentElement.prepend(element); + } + + /** + * @ngdoc service + * @name $animate + * @description The $animate service exposes a series of DOM utility methods that provide support + * for animation hooks. The default behavior is the application of DOM operations, however, + * when an animation is detected (and animations are enabled), $animate will do the heavy lifting + * to ensure that animation runs with the triggered DOM operation. + * + * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't + * included and only when it is active then the animation hooks that `$animate` triggers will be + * functional. Once active then all structural `ng-` directives will trigger animations as they perform + * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`, + * `ngShow`, `ngHide` and `ngMessages` also provide support for animations. + * + * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives. + * + * To learn more about enabling animation support, click here to visit the + * {@link ngAnimate ngAnimate module page}. + */ + return { + // we don't call it directly since non-existant arguments may + // be interpreted as null within the sub enabled function + + /** + * + * @ngdoc method + * @name $animate#on + * @kind function + * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...) + * has fired on the given element or among any of its children. Once the listener is fired, the provided callback + * is fired with the following params: + * + * ```js + * $animate.on('enter', container, + * function callback(element, phase) { + * // cool we detected an enter animation within the container + * } + * ); + * ``` + * + * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...) + * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself + * as well as among its children + * @param {Function} callback the callback function that will be fired when the listener is triggered + * + * The arguments present in the callback function are: + * * `element` - The captured DOM element that the animation was fired on. + * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends). + */ + on: $$animateQueue.on, + + /** + * + * @ngdoc method + * @name $animate#off + * @kind function + * @description Deregisters an event listener based on the event which has been associated with the provided element. This method + * can be used in three different ways depending on the arguments: + * + * ```js + * // remove all the animation event listeners listening for `enter` + * $animate.off('enter'); + * + * // remove all the animation event listeners listening for `enter` on the given element and its children + * $animate.off('enter', container); + * + * // remove the event listener function provided by `callback` that is set + * // to listen for `enter` on the given `container` as well as its children + * $animate.off('enter', container, callback); + * ``` + * + * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...) + * @param {DOMElement=} container the container element the event listener was placed on + * @param {Function=} callback the callback function that was registered as the listener + */ + off: $$animateQueue.off, + + /** + * @ngdoc method + * @name $animate#pin + * @kind function + * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists + * outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the + * element despite being outside the realm of the application or within another application. Say for example if the application + * was bootstrapped on an element that is somewhere inside of the `` tag, but we wanted to allow for an element to be situated + * as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind + * that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association. + * + * Note that this feature is only active when the `ngAnimate` module is used. + * + * @param {DOMElement} element the external element that will be pinned + * @param {DOMElement} parentElement the host parent element that will be associated with the external element + */ + pin: $$animateQueue.pin, + + /** + * + * @ngdoc method + * @name $animate#enabled + * @kind function + * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This + * function can be called in four ways: + * + * ```js + * // returns true or false + * $animate.enabled(); + * + * // changes the enabled state for all animations + * $animate.enabled(false); + * $animate.enabled(true); + * + * // returns true or false if animations are enabled for an element + * $animate.enabled(element); + * + * // changes the enabled state for an element and its children + * $animate.enabled(element, true); + * $animate.enabled(element, false); + * ``` + * + * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state + * @param {boolean=} enabled whether or not the animations will be enabled for the element + * + * @return {boolean} whether or not animations are enabled + */ + enabled: $$animateQueue.enabled, + + /** + * @ngdoc method + * @name $animate#cancel + * @kind function + * @description Cancels the provided animation. + * + * @param {Promise} animationPromise The animation promise that is returned when an animation is started. + */ + cancel: function(runner) { + runner.end && runner.end(); + }, + + /** + * + * @ngdoc method + * @name $animate#enter + * @kind function + * @description Inserts the element into the DOM either after the `after` element (if provided) or + * as the first child within the `parent` element and then triggers an animation. + * A promise is returned that will be resolved during the next digest once the animation + * has completed. + * + * @param {DOMElement} element the element which will be inserted into the DOM + * @param {DOMElement} parent the parent element which will append the element as + * a child (so long as the after element is not present) + * @param {DOMElement=} after the sibling element after which the element will be appended + * @param {object=} options an optional collection of options/styles that will be applied to the element + * + * @return {Promise} the animation callback promise + */ + enter: function(element, parent, after, options) { + parent = parent && jqLite(parent); + after = after && jqLite(after); + parent = parent || after.parent(); + domInsert(element, parent, after); + return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options)); + }, + + /** + * + * @ngdoc method + * @name $animate#move + * @kind function + * @description Inserts (moves) the element into its new position in the DOM either after + * the `after` element (if provided) or as the first child within the `parent` element + * and then triggers an animation. A promise is returned that will be resolved + * during the next digest once the animation has completed. + * + * @param {DOMElement} element the element which will be moved into the new DOM position + * @param {DOMElement} parent the parent element which will append the element as + * a child (so long as the after element is not present) + * @param {DOMElement=} after the sibling element after which the element will be appended + * @param {object=} options an optional collection of options/styles that will be applied to the element + * + * @return {Promise} the animation callback promise + */ + move: function(element, parent, after, options) { + parent = parent && jqLite(parent); + after = after && jqLite(after); + parent = parent || after.parent(); + domInsert(element, parent, after); + return $$animateQueue.push(element, 'move', prepareAnimateOptions(options)); + }, + + /** + * @ngdoc method + * @name $animate#leave + * @kind function + * @description Triggers an animation and then removes the element from the DOM. + * When the function is called a promise is returned that will be resolved during the next + * digest once the animation has completed. + * + * @param {DOMElement} element the element which will be removed from the DOM + * @param {object=} options an optional collection of options/styles that will be applied to the element + * + * @return {Promise} the animation callback promise + */ + leave: function(element, options) { + return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() { + element.remove(); + }); + }, + + /** + * @ngdoc method + * @name $animate#addClass + * @kind function + * + * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon + * execution, the addClass operation will only be handled after the next digest and it will not trigger an + * animation if element already contains the CSS class or if the class is removed at a later step. + * Note that class-based animations are treated differently compared to structural animations + * (like enter, move and leave) since the CSS classes may be added/removed at different points + * depending if CSS or JavaScript animations are used. + * + * @param {DOMElement} element the element which the CSS classes will be applied to + * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces) + * @param {object=} options an optional collection of options/styles that will be applied to the element + * + * @return {Promise} the animation callback promise + */ + addClass: function(element, className, options) { + options = prepareAnimateOptions(options); + options.addClass = mergeClasses(options.addclass, className); + return $$animateQueue.push(element, 'addClass', options); + }, + + /** + * @ngdoc method + * @name $animate#removeClass + * @kind function + * + * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon + * execution, the removeClass operation will only be handled after the next digest and it will not trigger an + * animation if element does not contain the CSS class or if the class is added at a later step. + * Note that class-based animations are treated differently compared to structural animations + * (like enter, move and leave) since the CSS classes may be added/removed at different points + * depending if CSS or JavaScript animations are used. + * + * @param {DOMElement} element the element which the CSS classes will be applied to + * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces) + * @param {object=} options an optional collection of options/styles that will be applied to the element + * + * @return {Promise} the animation callback promise + */ + removeClass: function(element, className, options) { + options = prepareAnimateOptions(options); + options.removeClass = mergeClasses(options.removeClass, className); + return $$animateQueue.push(element, 'removeClass', options); + }, + + /** + * @ngdoc method + * @name $animate#setClass + * @kind function + * + * @description Performs both the addition and removal of a CSS classes on an element and (during the process) + * triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and + * `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has + * passed. Note that class-based animations are treated differently compared to structural animations + * (like enter, move and leave) since the CSS classes may be added/removed at different points + * depending if CSS or JavaScript animations are used. + * + * @param {DOMElement} element the element which the CSS classes will be applied to + * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces) + * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces) + * @param {object=} options an optional collection of options/styles that will be applied to the element + * + * @return {Promise} the animation callback promise + */ + setClass: function(element, add, remove, options) { + options = prepareAnimateOptions(options); + options.addClass = mergeClasses(options.addClass, add); + options.removeClass = mergeClasses(options.removeClass, remove); + return $$animateQueue.push(element, 'setClass', options); + }, + + /** + * @ngdoc method + * @name $animate#animate + * @kind function + * + * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element. + * If any detected CSS transition, keyframe or JavaScript matches the provided className value, then the animation will take + * on the provided styles. For example, if a transition animation is set for the given classNamem, then the provided `from` and + * `to` styles will be applied alongside the given transition. If the CSS style provided in `from` does not have a corresponding + * style in `to`, the style in `from` is applied immediately, and no animation is run. + * If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate` + * method (or as part of the `options` parameter): + * + * ```js + * ngModule.animation('.my-inline-animation', function() { + * return { + * animate : function(element, from, to, done, options) { + * //animation + * done(); + * } + * } + * }); + * ``` + * + * @param {DOMElement} element the element which the CSS styles will be applied to + * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation. + * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation. + * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If + * this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element. + * (Note that if no animation is detected then this value will not be applied to the element.) + * @param {object=} options an optional collection of options/styles that will be applied to the element + * + * @return {Promise} the animation callback promise + */ + animate: function(element, from, to, className, options) { + options = prepareAnimateOptions(options); + options.from = options.from ? extend(options.from, from) : from; + options.to = options.to ? extend(options.to, to) : to; + + className = className || 'ng-inline-animate'; + options.tempClasses = mergeClasses(options.tempClasses, className); + return $$animateQueue.push(element, 'animate', options); + } + }; + }]; +}]; + +var $$AnimateAsyncRunFactoryProvider = function() { + this.$get = ['$$rAF', function($$rAF) { + var waitQueue = []; + + function waitForTick(fn) { + waitQueue.push(fn); + if (waitQueue.length > 1) return; + $$rAF(function() { + for (var i = 0; i < waitQueue.length; i++) { + waitQueue[i](); + } + waitQueue = []; + }); + } + + return function() { + var passed = false; + waitForTick(function() { + passed = true; + }); + return function(callback) { + passed ? callback() : waitForTick(callback); + }; + }; + }]; +}; + +var $$AnimateRunnerFactoryProvider = function() { + this.$get = ['$q', '$sniffer', '$$animateAsyncRun', '$document', '$timeout', + function($q, $sniffer, $$animateAsyncRun, $document, $timeout) { + + var INITIAL_STATE = 0; + var DONE_PENDING_STATE = 1; + var DONE_COMPLETE_STATE = 2; + + AnimateRunner.chain = function(chain, callback) { + var index = 0; + + next(); + function next() { + if (index === chain.length) { + callback(true); + return; + } + + chain[index](function(response) { + if (response === false) { + callback(false); + return; + } + index++; + next(); + }); + } + }; + + AnimateRunner.all = function(runners, callback) { + var count = 0; + var status = true; + forEach(runners, function(runner) { + runner.done(onProgress); + }); + + function onProgress(response) { + status = status && response; + if (++count === runners.length) { + callback(status); + } + } + }; + + function AnimateRunner(host) { + this.setHost(host); + + var rafTick = $$animateAsyncRun(); + var timeoutTick = function(fn) { + $timeout(fn, 0, false); + }; + + this._doneCallbacks = []; + this._tick = function(fn) { + var doc = $document[0]; + + // the document may not be ready or attached + // to the module for some internal tests + if (doc && doc.hidden) { + timeoutTick(fn); + } else { + rafTick(fn); + } + }; + this._state = 0; + } + + AnimateRunner.prototype = { + setHost: function(host) { + this.host = host || {}; + }, + + done: function(fn) { + if (this._state === DONE_COMPLETE_STATE) { + fn(); + } else { + this._doneCallbacks.push(fn); + } + }, + + progress: noop, + + getPromise: function() { + if (!this.promise) { + var self = this; + this.promise = $q(function(resolve, reject) { + self.done(function(status) { + status === false ? reject() : resolve(); + }); + }); + } + return this.promise; + }, + + then: function(resolveHandler, rejectHandler) { + return this.getPromise().then(resolveHandler, rejectHandler); + }, + + 'catch': function(handler) { + return this.getPromise()['catch'](handler); + }, + + 'finally': function(handler) { + return this.getPromise()['finally'](handler); + }, + + pause: function() { + if (this.host.pause) { + this.host.pause(); + } + }, + + resume: function() { + if (this.host.resume) { + this.host.resume(); + } + }, + + end: function() { + if (this.host.end) { + this.host.end(); + } + this._resolve(true); + }, + + cancel: function() { + if (this.host.cancel) { + this.host.cancel(); + } + this._resolve(false); + }, + + complete: function(response) { + var self = this; + if (self._state === INITIAL_STATE) { + self._state = DONE_PENDING_STATE; + self._tick(function() { + self._resolve(response); + }); + } + }, + + _resolve: function(response) { + if (this._state !== DONE_COMPLETE_STATE) { + forEach(this._doneCallbacks, function(fn) { + fn(response); + }); + this._doneCallbacks.length = 0; + this._state = DONE_COMPLETE_STATE; + } + } + }; + + return AnimateRunner; + }]; +}; + +/** + * @ngdoc service + * @name $animateCss + * @kind object + * + * @description + * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included, + * then the `$animateCss` service will actually perform animations. + * + * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}. + */ +var $CoreAnimateCssProvider = function() { + this.$get = ['$$rAF', '$q', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) { + + return function(element, initialOptions) { + // all of the animation functions should create + // a copy of the options data, however, if a + // parent service has already created a copy then + // we should stick to using that + var options = initialOptions || {}; + if (!options.$$prepared) { + options = copy(options); + } + + // there is no point in applying the styles since + // there is no animation that goes on at all in + // this version of $animateCss. + if (options.cleanupStyles) { + options.from = options.to = null; + } + + if (options.from) { + element.css(options.from); + options.from = null; + } + + /* jshint newcap: false */ + var closed, runner = new $$AnimateRunner(); + return { + start: run, + end: run + }; + + function run() { + $$rAF(function() { + applyAnimationContents(); + if (!closed) { + runner.complete(); + } + closed = true; + }); + return runner; + } + + function applyAnimationContents() { + if (options.addClass) { + element.addClass(options.addClass); + options.addClass = null; + } + if (options.removeClass) { + element.removeClass(options.removeClass); + options.removeClass = null; + } + if (options.to) { + element.css(options.to); + options.to = null; + } + } + }; + }]; +}; + +/* global stripHash: true */ + +/** + * ! This is a private undocumented service ! + * + * @name $browser + * @requires $log + * @description + * This object has two goals: + * + * - hide all the global state in the browser caused by the window object + * - abstract away all the browser specific features and inconsistencies + * + * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser` + * service, which can be used for convenient testing of the application without the interaction with + * the real browser apis. + */ +/** + * @param {object} window The global window object. + * @param {object} document jQuery wrapped document. + * @param {object} $log window.console or an object with the same interface. + * @param {object} $sniffer $sniffer service + */ +function Browser(window, document, $log, $sniffer) { + var self = this, + location = window.location, + history = window.history, + setTimeout = window.setTimeout, + clearTimeout = window.clearTimeout, + pendingDeferIds = {}; + + self.isMock = false; + + var outstandingRequestCount = 0; + var outstandingRequestCallbacks = []; + + // TODO(vojta): remove this temporary api + self.$$completeOutstandingRequest = completeOutstandingRequest; + self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; }; + + /** + * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks` + * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed. + */ + function completeOutstandingRequest(fn) { + try { + fn.apply(null, sliceArgs(arguments, 1)); + } finally { + outstandingRequestCount--; + if (outstandingRequestCount === 0) { + while (outstandingRequestCallbacks.length) { + try { + outstandingRequestCallbacks.pop()(); + } catch (e) { + $log.error(e); + } + } + } + } + } + + function getHash(url) { + var index = url.indexOf('#'); + return index === -1 ? '' : url.substr(index); + } + + /** + * @private + * Note: this method is used only by scenario runner + * TODO(vojta): prefix this method with $$ ? + * @param {function()} callback Function that will be called when no outstanding request + */ + self.notifyWhenNoOutstandingRequests = function(callback) { + if (outstandingRequestCount === 0) { + callback(); + } else { + outstandingRequestCallbacks.push(callback); + } + }; + + ////////////////////////////////////////////////////////////// + // URL API + ////////////////////////////////////////////////////////////// + + var cachedState, lastHistoryState, + lastBrowserUrl = location.href, + baseElement = document.find('base'), + pendingLocation = null, + getCurrentState = !$sniffer.history ? noop : function getCurrentState() { + try { + return history.state; + } catch (e) { + // MSIE can reportedly throw when there is no state (UNCONFIRMED). + } + }; + + cacheState(); + lastHistoryState = cachedState; + + /** + * @name $browser#url + * + * @description + * GETTER: + * Without any argument, this method just returns current value of location.href. + * + * SETTER: + * With at least one argument, this method sets url to new value. + * If html5 history api supported, pushState/replaceState is used, otherwise + * location.href/location.replace is used. + * Returns its own instance to allow chaining + * + * NOTE: this api is intended for use only by the $location service. Please use the + * {@link ng.$location $location service} to change url. + * + * @param {string} url New url (when used as setter) + * @param {boolean=} replace Should new url replace current history record? + * @param {object=} state object to use with pushState/replaceState + */ + self.url = function(url, replace, state) { + // In modern browsers `history.state` is `null` by default; treating it separately + // from `undefined` would cause `$browser.url('/foo')` to change `history.state` + // to undefined via `pushState`. Instead, let's change `undefined` to `null` here. + if (isUndefined(state)) { + state = null; + } + + // Android Browser BFCache causes location, history reference to become stale. + if (location !== window.location) location = window.location; + if (history !== window.history) history = window.history; + + // setter + if (url) { + var sameState = lastHistoryState === state; + + // Don't change anything if previous and current URLs and states match. This also prevents + // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode. + // See https://github.com/angular/angular.js/commit/ffb2701 + if (lastBrowserUrl === url && (!$sniffer.history || sameState)) { + return self; + } + var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url); + lastBrowserUrl = url; + lastHistoryState = state; + // Don't use history API if only the hash changed + // due to a bug in IE10/IE11 which leads + // to not firing a `hashchange` nor `popstate` event + // in some cases (see #9143). + if ($sniffer.history && (!sameBase || !sameState)) { + history[replace ? 'replaceState' : 'pushState'](state, '', url); + cacheState(); + // Do the assignment again so that those two variables are referentially identical. + lastHistoryState = cachedState; + } else { + if (!sameBase || pendingLocation) { + pendingLocation = url; + } + if (replace) { + location.replace(url); + } else if (!sameBase) { + location.href = url; + } else { + location.hash = getHash(url); + } + if (location.href !== url) { + pendingLocation = url; + } + } + return self; + // getter + } else { + // - pendingLocation is needed as browsers don't allow to read out + // the new location.href if a reload happened or if there is a bug like in iOS 9 (see + // https://openradar.appspot.com/22186109). + // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172 + return pendingLocation || location.href.replace(/%27/g,"'"); + } + }; + + /** + * @name $browser#state + * + * @description + * This method is a getter. + * + * Return history.state or null if history.state is undefined. + * + * @returns {object} state + */ + self.state = function() { + return cachedState; + }; + + var urlChangeListeners = [], + urlChangeInit = false; + + function cacheStateAndFireUrlChange() { + pendingLocation = null; + cacheState(); + fireUrlChange(); + } + + // This variable should be used *only* inside the cacheState function. + var lastCachedState = null; + function cacheState() { + // This should be the only place in $browser where `history.state` is read. + cachedState = getCurrentState(); + cachedState = isUndefined(cachedState) ? null : cachedState; + + // Prevent callbacks fo fire twice if both hashchange & popstate were fired. + if (equals(cachedState, lastCachedState)) { + cachedState = lastCachedState; + } + lastCachedState = cachedState; + } + + function fireUrlChange() { + if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) { + return; + } + + lastBrowserUrl = self.url(); + lastHistoryState = cachedState; + forEach(urlChangeListeners, function(listener) { + listener(self.url(), cachedState); + }); + } + + /** + * @name $browser#onUrlChange + * + * @description + * Register callback function that will be called, when url changes. + * + * It's only called when the url is changed from outside of angular: + * - user types different url into address bar + * - user clicks on history (forward/back) button + * - user clicks on a link + * + * It's not called when url is changed by $browser.url() method + * + * The listener gets called with new url as parameter. + * + * NOTE: this api is intended for use only by the $location service. Please use the + * {@link ng.$location $location service} to monitor url changes in angular apps. + * + * @param {function(string)} listener Listener function to be called when url changes. + * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous. + */ + self.onUrlChange = function(callback) { + // TODO(vojta): refactor to use node's syntax for events + if (!urlChangeInit) { + // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera) + // don't fire popstate when user change the address bar and don't fire hashchange when url + // changed by push/replaceState + + // html5 history api - popstate event + if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange); + // hashchange event + jqLite(window).on('hashchange', cacheStateAndFireUrlChange); + + urlChangeInit = true; + } + + urlChangeListeners.push(callback); + return callback; + }; + + /** + * @private + * Remove popstate and hashchange handler from window. + * + * NOTE: this api is intended for use only by $rootScope. + */ + self.$$applicationDestroyed = function() { + jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange); + }; + + /** + * Checks whether the url has changed outside of Angular. + * Needs to be exported to be able to check for changes that have been done in sync, + * as hashchange/popstate events fire in async. + */ + self.$$checkUrlChange = fireUrlChange; + + ////////////////////////////////////////////////////////////// + // Misc API + ////////////////////////////////////////////////////////////// + + /** + * @name $browser#baseHref + * + * @description + * Returns current + * (always relative - without domain) + * + * @returns {string} The current base href + */ + self.baseHref = function() { + var href = baseElement.attr('href'); + return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : ''; + }; + + /** + * @name $browser#defer + * @param {function()} fn A function, who's execution should be deferred. + * @param {number=} [delay=0] of milliseconds to defer the function execution. + * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`. + * + * @description + * Executes a fn asynchronously via `setTimeout(fn, delay)`. + * + * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using + * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed + * via `$browser.defer.flush()`. + * + */ + self.defer = function(fn, delay) { + var timeoutId; + outstandingRequestCount++; + timeoutId = setTimeout(function() { + delete pendingDeferIds[timeoutId]; + completeOutstandingRequest(fn); + }, delay || 0); + pendingDeferIds[timeoutId] = true; + return timeoutId; + }; + + + /** + * @name $browser#defer.cancel + * + * @description + * Cancels a deferred task identified with `deferId`. + * + * @param {*} deferId Token returned by the `$browser.defer` function. + * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully + * canceled. + */ + self.defer.cancel = function(deferId) { + if (pendingDeferIds[deferId]) { + delete pendingDeferIds[deferId]; + clearTimeout(deferId); + completeOutstandingRequest(noop); + return true; + } + return false; + }; + +} + +function $BrowserProvider() { + this.$get = ['$window', '$log', '$sniffer', '$document', + function($window, $log, $sniffer, $document) { + return new Browser($window, $document, $log, $sniffer); + }]; +} + +/** + * @ngdoc service + * @name $cacheFactory + * + * @description + * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to + * them. + * + * ```js + * + * var cache = $cacheFactory('cacheId'); + * expect($cacheFactory.get('cacheId')).toBe(cache); + * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined(); + * + * cache.put("key", "value"); + * cache.put("another key", "another value"); + * + * // We've specified no options on creation + * expect(cache.info()).toEqual({id: 'cacheId', size: 2}); + * + * ``` + * + * + * @param {string} cacheId Name or id of the newly created cache. + * @param {object=} options Options object that specifies the cache behavior. Properties: + * + * - `{number=}` `capacity` — turns the cache into LRU cache. + * + * @returns {object} Newly created cache object with the following set of methods: + * + * - `{object}` `info()` — Returns id, size, and options of cache. + * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns + * it. + * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss. + * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache. + * - `{void}` `removeAll()` — Removes all cached values. + * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory. + * + * @example + + +
    + + + + +

    Cached Values

    +
    + + : + +
    + +

    Cache Info

    +
    + + : + +
    +
    +
    + + angular.module('cacheExampleApp', []). + controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) { + $scope.keys = []; + $scope.cache = $cacheFactory('cacheId'); + $scope.put = function(key, value) { + if (angular.isUndefined($scope.cache.get(key))) { + $scope.keys.push(key); + } + $scope.cache.put(key, angular.isUndefined(value) ? null : value); + }; + }]); + + + p { + margin: 10px 0 3px; + } + +
    + */ +function $CacheFactoryProvider() { + + this.$get = function() { + var caches = {}; + + function cacheFactory(cacheId, options) { + if (cacheId in caches) { + throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId); + } + + var size = 0, + stats = extend({}, options, {id: cacheId}), + data = createMap(), + capacity = (options && options.capacity) || Number.MAX_VALUE, + lruHash = createMap(), + freshEnd = null, + staleEnd = null; + + /** + * @ngdoc type + * @name $cacheFactory.Cache + * + * @description + * A cache object used to store and retrieve data, primarily used by + * {@link $http $http} and the {@link ng.directive:script script} directive to cache + * templates and other data. + * + * ```js + * angular.module('superCache') + * .factory('superCache', ['$cacheFactory', function($cacheFactory) { + * return $cacheFactory('super-cache'); + * }]); + * ``` + * + * Example test: + * + * ```js + * it('should behave like a cache', inject(function(superCache) { + * superCache.put('key', 'value'); + * superCache.put('another key', 'another value'); + * + * expect(superCache.info()).toEqual({ + * id: 'super-cache', + * size: 2 + * }); + * + * superCache.remove('another key'); + * expect(superCache.get('another key')).toBeUndefined(); + * + * superCache.removeAll(); + * expect(superCache.info()).toEqual({ + * id: 'super-cache', + * size: 0 + * }); + * })); + * ``` + */ + return caches[cacheId] = { + + /** + * @ngdoc method + * @name $cacheFactory.Cache#put + * @kind function + * + * @description + * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be + * retrieved later, and incrementing the size of the cache if the key was not already + * present in the cache. If behaving like an LRU cache, it will also remove stale + * entries from the set. + * + * It will not insert undefined values into the cache. + * + * @param {string} key the key under which the cached data is stored. + * @param {*} value the value to store alongside the key. If it is undefined, the key + * will not be stored. + * @returns {*} the value stored. + */ + put: function(key, value) { + if (isUndefined(value)) return; + if (capacity < Number.MAX_VALUE) { + var lruEntry = lruHash[key] || (lruHash[key] = {key: key}); + + refresh(lruEntry); + } + + if (!(key in data)) size++; + data[key] = value; + + if (size > capacity) { + this.remove(staleEnd.key); + } + + return value; + }, + + /** + * @ngdoc method + * @name $cacheFactory.Cache#get + * @kind function + * + * @description + * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object. + * + * @param {string} key the key of the data to be retrieved + * @returns {*} the value stored. + */ + get: function(key) { + if (capacity < Number.MAX_VALUE) { + var lruEntry = lruHash[key]; + + if (!lruEntry) return; + + refresh(lruEntry); + } + + return data[key]; + }, + + + /** + * @ngdoc method + * @name $cacheFactory.Cache#remove + * @kind function + * + * @description + * Removes an entry from the {@link $cacheFactory.Cache Cache} object. + * + * @param {string} key the key of the entry to be removed + */ + remove: function(key) { + if (capacity < Number.MAX_VALUE) { + var lruEntry = lruHash[key]; + + if (!lruEntry) return; + + if (lruEntry == freshEnd) freshEnd = lruEntry.p; + if (lruEntry == staleEnd) staleEnd = lruEntry.n; + link(lruEntry.n,lruEntry.p); + + delete lruHash[key]; + } + + if (!(key in data)) return; + + delete data[key]; + size--; + }, + + + /** + * @ngdoc method + * @name $cacheFactory.Cache#removeAll + * @kind function + * + * @description + * Clears the cache object of any entries. + */ + removeAll: function() { + data = createMap(); + size = 0; + lruHash = createMap(); + freshEnd = staleEnd = null; + }, + + + /** + * @ngdoc method + * @name $cacheFactory.Cache#destroy + * @kind function + * + * @description + * Destroys the {@link $cacheFactory.Cache Cache} object entirely, + * removing it from the {@link $cacheFactory $cacheFactory} set. + */ + destroy: function() { + data = null; + stats = null; + lruHash = null; + delete caches[cacheId]; + }, + + + /** + * @ngdoc method + * @name $cacheFactory.Cache#info + * @kind function + * + * @description + * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}. + * + * @returns {object} an object with the following properties: + *
      + *
    • **id**: the id of the cache instance
    • + *
    • **size**: the number of entries kept in the cache instance
    • + *
    • **...**: any additional properties from the options object when creating the + * cache.
    • + *
    + */ + info: function() { + return extend({}, stats, {size: size}); + } + }; + + + /** + * makes the `entry` the freshEnd of the LRU linked list + */ + function refresh(entry) { + if (entry != freshEnd) { + if (!staleEnd) { + staleEnd = entry; + } else if (staleEnd == entry) { + staleEnd = entry.n; + } + + link(entry.n, entry.p); + link(entry, freshEnd); + freshEnd = entry; + freshEnd.n = null; + } + } + + + /** + * bidirectionally links two entries of the LRU linked list + */ + function link(nextEntry, prevEntry) { + if (nextEntry != prevEntry) { + if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify + if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify + } + } + } + + + /** + * @ngdoc method + * @name $cacheFactory#info + * + * @description + * Get information about all the caches that have been created + * + * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info` + */ + cacheFactory.info = function() { + var info = {}; + forEach(caches, function(cache, cacheId) { + info[cacheId] = cache.info(); + }); + return info; + }; + + + /** + * @ngdoc method + * @name $cacheFactory#get + * + * @description + * Get access to a cache object by the `cacheId` used when it was created. + * + * @param {string} cacheId Name or id of a cache to access. + * @returns {object} Cache object identified by the cacheId or undefined if no such cache. + */ + cacheFactory.get = function(cacheId) { + return caches[cacheId]; + }; + + + return cacheFactory; + }; +} + +/** + * @ngdoc service + * @name $templateCache + * + * @description + * The first time a template is used, it is loaded in the template cache for quick retrieval. You + * can load templates directly into the cache in a `script` tag, or by consuming the + * `$templateCache` service directly. + * + * Adding via the `script` tag: + * + * ```html + * + * ``` + * + * **Note:** the `script` tag containing the template does not need to be included in the `head` of + * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE, + * element with ng-app attribute), otherwise the template will be ignored. + * + * Adding via the `$templateCache` service: + * + * ```js + * var myApp = angular.module('myApp', []); + * myApp.run(function($templateCache) { + * $templateCache.put('templateId.html', 'This is the content of the template'); + * }); + * ``` + * + * To retrieve the template later, simply use it in your HTML: + * ```html + *
    + * ``` + * + * or get it via Javascript: + * ```js + * $templateCache.get('templateId.html') + * ``` + * + * See {@link ng.$cacheFactory $cacheFactory}. + * + */ +function $TemplateCacheProvider() { + this.$get = ['$cacheFactory', function($cacheFactory) { + return $cacheFactory('templates'); + }]; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Any commits to this file should be reviewed with security in mind. * + * Changes to this file can potentially create security vulnerabilities. * + * An approval from 2 Core members with history of modifying * + * this file is required. * + * * + * Does the change somehow allow for arbitrary javascript to be executed? * + * Or allows for someone to change the prototype of built-in objects? * + * Or gives undesired access to variables likes document or window? * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE! + * + * DOM-related variables: + * + * - "node" - DOM Node + * - "element" - DOM Element or Node + * - "$node" or "$element" - jqLite-wrapped node or element + * + * + * Compiler related stuff: + * + * - "linkFn" - linking fn of a single directive + * - "nodeLinkFn" - function that aggregates all linking fns for a particular node + * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node + * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList) + */ + + +/** + * @ngdoc service + * @name $compile + * @kind function + * + * @description + * Compiles an HTML string or DOM into a template and produces a template function, which + * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together. + * + * The compilation is a process of walking the DOM tree and matching DOM elements to + * {@link ng.$compileProvider#directive directives}. + * + *
    + * **Note:** This document is an in-depth reference of all directive options. + * For a gentle introduction to directives with examples of common use cases, + * see the {@link guide/directive directive guide}. + *
    + * + * ## Comprehensive Directive API + * + * There are many different options for a directive. + * + * The difference resides in the return value of the factory function. + * You can either return a "Directive Definition Object" (see below) that defines the directive properties, + * or just the `postLink` function (all other properties will have the default values). + * + *
    + * **Best Practice:** It's recommended to use the "directive definition object" form. + *
    + * + * Here's an example directive declared with a Directive Definition Object: + * + * ```js + * var myModule = angular.module(...); + * + * myModule.directive('directiveName', function factory(injectables) { + * var directiveDefinitionObject = { + * priority: 0, + * template: '
    ', // or // function(tElement, tAttrs) { ... }, + * // or + * // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... }, + * transclude: false, + * restrict: 'A', + * templateNamespace: 'html', + * scope: false, + * controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... }, + * controllerAs: 'stringIdentifier', + * bindToController: false, + * require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'], + * compile: function compile(tElement, tAttrs, transclude) { + * return { + * pre: function preLink(scope, iElement, iAttrs, controller) { ... }, + * post: function postLink(scope, iElement, iAttrs, controller) { ... } + * } + * // or + * // return function postLink( ... ) { ... } + * }, + * // or + * // link: { + * // pre: function preLink(scope, iElement, iAttrs, controller) { ... }, + * // post: function postLink(scope, iElement, iAttrs, controller) { ... } + * // } + * // or + * // link: function postLink( ... ) { ... } + * }; + * return directiveDefinitionObject; + * }); + * ``` + * + *
    + * **Note:** Any unspecified options will use the default value. You can see the default values below. + *
    + * + * Therefore the above can be simplified as: + * + * ```js + * var myModule = angular.module(...); + * + * myModule.directive('directiveName', function factory(injectables) { + * var directiveDefinitionObject = { + * link: function postLink(scope, iElement, iAttrs) { ... } + * }; + * return directiveDefinitionObject; + * // or + * // return function postLink(scope, iElement, iAttrs) { ... } + * }); + * ``` + * + * + * + * ### Directive Definition Object + * + * The directive definition object provides instructions to the {@link ng.$compile + * compiler}. The attributes are: + * + * #### `multiElement` + * When this property is set to true, the HTML compiler will collect DOM nodes between + * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them + * together as the directive elements. It is recommended that this feature be used on directives + * which are not strictly behavioral (such as {@link ngClick}), and which + * do not manipulate or replace child nodes (such as {@link ngInclude}). + * + * #### `priority` + * When there are multiple directives defined on a single DOM element, sometimes it + * is necessary to specify the order in which the directives are applied. The `priority` is used + * to sort the directives before their `compile` functions get called. Priority is defined as a + * number. Directives with greater numerical `priority` are compiled first. Pre-link functions + * are also run in priority order, but post-link functions are run in reverse order. The order + * of directives with the same priority is undefined. The default priority is `0`. + * + * #### `terminal` + * If set to true then the current `priority` will be the last set of directives + * which will execute (any directives at the current priority will still execute + * as the order of execution on same `priority` is undefined). Note that expressions + * and other directives used in the directive's template will also be excluded from execution. + * + * #### `scope` + * The scope property can be `true`, an object or a falsy value: + * + * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope. + * + * * **`true`:** A new child scope that prototypically inherits from its parent will be created for + * the directive's element. If multiple directives on the same element request a new scope, + * only one new scope is created. The new scope rule does not apply for the root of the template + * since the root of the template always gets a new scope. + * + * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The + * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent + * scope. This is useful when creating reusable components, which should not accidentally read or modify + * data in the parent scope. + * + * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the + * directive's element. These local properties are useful for aliasing values for templates. The keys in + * the object hash map to the name of the property on the isolate scope; the values define how the property + * is bound to the parent scope, via matching attributes on the directive's element: + * + * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is + * always a string since DOM attributes are strings. If no `attr` name is specified then the + * attribute name is assumed to be the same as the local name. Given `` and the isolate scope definition `scope: { localName:'@myAttr' }`, + * the directive's scope property `localName` will reflect the interpolated value of `hello + * {{name}}`. As the `name` attribute changes so will the `localName` property on the directive's + * scope. The `name` is read from the parent scope (not the directive's scope). + * + * * `=` or `=attr` - set up a bidirectional binding between a local scope property and an expression + * passed via the attribute `attr`. The expression is evaluated in the context of the parent scope. + * If no `attr` name is specified then the attribute name is assumed to be the same as the local + * name. Given `` and the isolate scope definition `scope: { + * localModel: '=myAttr' }`, the property `localModel` on the directive's scope will reflect the + * value of `parentModel` on the parent scope. Changes to `parentModel` will be reflected in + * `localModel` and vice versa. Optional attributes should be marked as such with a question mark: + * `=?` or `=?attr`. If the binding expression is non-assignable, or if the attribute isn't + * optional and doesn't exist, an exception ({@link error/$compile/nonassign `$compile:nonassign`}) + * will be thrown upon discovering changes to the local value, since it will be impossible to sync + * them back to the parent scope. By default, the {@link ng.$rootScope.Scope#$watch `$watch`} + * method is used for tracking changes, and the equality check is based on object identity. + * However, if an object literal or an array literal is passed as the binding expression, the + * equality check is done by value (using the {@link angular.equals} function). It's also possible + * to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection + * `$watchCollection`}: use `=*` or `=*attr` (`=*?` or `=*?attr` if the attribute is optional). + * + * * `<` or `` and directive definition of + * `scope: { localModel:'` and the isolate scope definition `scope: { + * localFn:'&myAttr' }`, the isolate scope property `localFn` will point to a function wrapper for + * the `count = count + value` expression. Often it's desirable to pass data from the isolated scope + * via an expression to the parent scope. This can be done by passing a map of local variable names + * and values into the expression wrapper fn. For example, if the expression is `increment(amount)` + * then we can specify the amount value by calling the `localFn` as `localFn({amount: 22})`. + * + * In general it's possible to apply more than one directive to one element, but there might be limitations + * depending on the type of scope required by the directives. The following points will help explain these limitations. + * For simplicity only two directives are taken into account, but it is also applicable for several directives: + * + * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope + * * **child scope** + **no scope** => Both directives will share one single child scope + * * **child scope** + **child scope** => Both directives will share one single child scope + * * **isolated scope** + **no scope** => The isolated directive will use it's own created isolated scope. The other directive will use + * its parent's scope + * * **isolated scope** + **child scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives cannot + * be applied to the same element. + * * **isolated scope** + **isolated scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives + * cannot be applied to the same element. + * + * + * #### `bindToController` + * This property is used to bind scope properties directly to the controller. It can be either + * `true` or an object hash with the same format as the `scope` property. Additionally, a controller + * alias must be set, either by using `controllerAs: 'myAlias'` or by specifying the alias in the controller + * definition: `controller: 'myCtrl as myAlias'`. + * + * When an isolate scope is used for a directive (see above), `bindToController: true` will + * allow a component to have its properties bound to the controller, rather than to scope. + * + * After the controller is instantiated, the initial values of the isolate scope bindings will be bound to the controller + * properties. You can access these bindings once they have been initialized by providing a controller method called + * `$onInit`, which is called after all the controllers on an element have been constructed and had their bindings + * initialized. + * + *
    + * **Deprecation warning:** although bindings for non-ES6 class controllers are currently + * bound to `this` before the controller constructor is called, this use is now deprecated. Please place initialization + * code that relies upon bindings inside a `$onInit` method on the controller, instead. + *
    + * + * It is also possible to set `bindToController` to an object hash with the same format as the `scope` property. + * This will set up the scope bindings to the controller directly. Note that `scope` can still be used + * to define which kind of scope is created. By default, no scope is created. Use `scope: {}` to create an isolate + * scope (useful for component directives). + * + * If both `bindToController` and `scope` are defined and have object hashes, `bindToController` overrides `scope`. + * + * + * #### `controller` + * Controller constructor function. The controller is instantiated before the + * pre-linking phase and can be accessed by other directives (see + * `require` attribute). This allows the directives to communicate with each other and augment + * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals: + * + * * `$scope` - Current scope associated with the element + * * `$element` - Current element + * * `$attrs` - Current attributes object for the element + * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope: + * `function([scope], cloneLinkingFn, futureParentElement, slotName)`: + * * `scope`: (optional) override the scope. + * * `cloneLinkingFn`: (optional) argument to create clones of the original transcluded content. + * * `futureParentElement` (optional): + * * defines the parent to which the `cloneLinkingFn` will add the cloned elements. + * * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`. + * * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements) + * and when the `cloneLinkinFn` is passed, + * as those elements need to created and cloned in a special way when they are defined outside their + * usual containers (e.g. like ``). + * * See also the `directive.templateNamespace` property. + * * `slotName`: (optional) the name of the slot to transclude. If falsy (e.g. `null`, `undefined` or `''`) + * then the default translusion is provided. + * The `$transclude` function also has a method on it, `$transclude.isSlotFilled(slotName)`, which returns + * `true` if the specified slot contains content (i.e. one or more DOM nodes). + * + * The controller can provide the following methods that act as life-cycle hooks: + * * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and + * had their bindings initialized (and before the pre & post linking functions for the directives on + * this element). This is a good place to put initialization code for your controller. + * * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The + * `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an + * object of the form `{ currentValue: ..., previousValue: ... }`. Use this hook to trigger updates within a component + * such as cloning the bound value to prevent accidental mutation of the outer value. + * * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing + * external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in + * the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent + * components will have their `$onDestroy()` hook called before child components. + * * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link + * function this hook can be used to set up DOM event handlers and do direct DOM manipulation. + * Note that child elements that contain `templateUrl` directives will not have been compiled and linked since + * they are waiting for their template to load asynchronously and their own compilation and linking has been + * suspended until that occurs. + * + * + * #### `require` + * Require another directive and inject its controller as the fourth argument to the linking function. The + * `require` property can be a string, an array or an object: + * * a **string** containing the name of the directive to pass to the linking function + * * an **array** containing the names of directives to pass to the linking function. The argument passed to the + * linking function will be an array of controllers in the same order as the names in the `require` property + * * an **object** whose property values are the names of the directives to pass to the linking function. The argument + * passed to the linking function will also be an object with matching keys, whose values will hold the corresponding + * controllers. + * + * If the `require` property is an object and `bindToController` is truthy, then the required controllers are + * bound to the controller using the keys of the `require` property. This binding occurs after all the controllers + * have been constructed but before `$onInit` is called. + * See the {@link $compileProvider#component} helper for an example of how this can be used. + * + * If no such required directive(s) can be found, or if the directive does not have a controller, then an error is + * raised (unless no link function is specified and the required controllers are not being bound to the directive + * controller, in which case error checking is skipped). The name can be prefixed with: + * + * * (no prefix) - Locate the required controller on the current element. Throw an error if not found. + * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found. + * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found. + * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found. + * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass + * `null` to the `link` fn if not found. + * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass + * `null` to the `link` fn if not found. + * + * + * #### `controllerAs` + * Identifier name for a reference to the controller in the directive's scope. + * This allows the controller to be referenced from the directive template. This is especially + * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible + * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the + * `controllerAs` reference might overwrite a property that already exists on the parent scope. + * + * + * #### `restrict` + * String of subset of `EACM` which restricts the directive to a specific directive + * declaration style. If omitted, the defaults (elements and attributes) are used. + * + * * `E` - Element name (default): `` + * * `A` - Attribute (default): `
    ` + * * `C` - Class: `
    ` + * * `M` - Comment: `` + * + * + * #### `templateNamespace` + * String representing the document type used by the markup in the template. + * AngularJS needs this information as those elements need to be created and cloned + * in a special way when they are defined outside their usual containers like `` and ``. + * + * * `html` - All root nodes in the template are HTML. Root nodes may also be + * top-level elements such as `` or ``. + * * `svg` - The root nodes in the template are SVG elements (excluding ``). + * * `math` - The root nodes in the template are MathML elements (excluding ``). + * + * If no `templateNamespace` is specified, then the namespace is considered to be `html`. + * + * #### `template` + * HTML markup that may: + * * Replace the contents of the directive's element (default). + * * Replace the directive's element itself (if `replace` is true - DEPRECATED). + * * Wrap the contents of the directive's element (if `transclude` is true). + * + * Value may be: + * + * * A string. For example `
    {{delete_str}}
    `. + * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile` + * function api below) and returns a string value. + * + * + * #### `templateUrl` + * This is similar to `template` but the template is loaded from the specified URL, asynchronously. + * + * Because template loading is asynchronous the compiler will suspend compilation of directives on that element + * for later when the template has been resolved. In the meantime it will continue to compile and link + * sibling and parent elements as though this element had not contained any directives. + * + * The compiler does not suspend the entire compilation to wait for templates to be loaded because this + * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the + * case when only one deeply nested directive has `templateUrl`. + * + * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache} + * + * You can specify `templateUrl` as a string representing the URL or as a function which takes two + * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns + * a string value representing the url. In either case, the template URL is passed through {@link + * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}. + * + * + * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0) + * specify what the template should replace. Defaults to `false`. + * + * * `true` - the template will replace the directive's element. + * * `false` - the template will replace the contents of the directive's element. + * + * The replacement process migrates all of the attributes / classes from the old element to the new + * one. See the {@link guide/directive#template-expanding-directive + * Directives Guide} for an example. + * + * There are very few scenarios where element replacement is required for the application function, + * the main one being reusable custom components that are used within SVG contexts + * (because SVG doesn't work with custom elements in the DOM tree). + * + * #### `transclude` + * Extract the contents of the element where the directive appears and make it available to the directive. + * The contents are compiled and provided to the directive as a **transclusion function**. See the + * {@link $compile#transclusion Transclusion} section below. + * + * + * #### `compile` + * + * ```js + * function compile(tElement, tAttrs, transclude) { ... } + * ``` + * + * The compile function deals with transforming the template DOM. Since most directives do not do + * template transformation, it is not used often. The compile function takes the following arguments: + * + * * `tElement` - template element - The element where the directive has been declared. It is + * safe to do template transformation on the element and child elements only. + * + * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared + * between all directive compile functions. + * + * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)` + * + *
    + * **Note:** The template instance and the link instance may be different objects if the template has + * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that + * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration + * should be done in a linking function rather than in a compile function. + *
    + + *
    + * **Note:** The compile function cannot handle directives that recursively use themselves in their + * own templates or compile functions. Compiling these directives results in an infinite loop and + * stack overflow errors. + * + * This can be avoided by manually using $compile in the postLink function to imperatively compile + * a directive's template instead of relying on automatic template compilation via `template` or + * `templateUrl` declaration or manual compilation inside the compile function. + *
    + * + *
    + * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it + * e.g. does not know about the right outer scope. Please use the transclude function that is passed + * to the link function instead. + *
    + + * A compile function can have a return value which can be either a function or an object. + * + * * returning a (post-link) function - is equivalent to registering the linking function via the + * `link` property of the config object when the compile function is empty. + * + * * returning an object with function(s) registered via `pre` and `post` properties - allows you to + * control when a linking function should be called during the linking phase. See info about + * pre-linking and post-linking functions below. + * + * + * #### `link` + * This property is used only if the `compile` property is not defined. + * + * ```js + * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... } + * ``` + * + * The link function is responsible for registering DOM listeners as well as updating the DOM. It is + * executed after the template has been cloned. This is where most of the directive logic will be + * put. + * + * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the + * directive for registering {@link ng.$rootScope.Scope#$watch watches}. + * + * * `iElement` - instance element - The element where the directive is to be used. It is safe to + * manipulate the children of the element only in `postLink` function since the children have + * already been linked. + * + * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared + * between all directive linking functions. + * + * * `controller` - the directive's required controller instance(s) - Instances are shared + * among all directives, which allows the directives to use the controllers as a communication + * channel. The exact value depends on the directive's `require` property: + * * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one + * * `string`: the controller instance + * * `array`: array of controller instances + * + * If a required controller cannot be found, and it is optional, the instance is `null`, + * otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown. + * + * Note that you can also require the directive's own controller - it will be made available like + * any other controller. + * + * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope. + * This is the same as the `$transclude` + * parameter of directive controllers, see there for details. + * `function([scope], cloneLinkingFn, futureParentElement)`. + * + * #### Pre-linking function + * + * Executed before the child elements are linked. Not safe to do DOM transformation since the + * compiler linking function will fail to locate the correct elements for linking. + * + * #### Post-linking function + * + * Executed after the child elements are linked. + * + * Note that child elements that contain `templateUrl` directives will not have been compiled + * and linked since they are waiting for their template to load asynchronously and their own + * compilation and linking has been suspended until that occurs. + * + * It is safe to do DOM transformation in the post-linking function on elements that are not waiting + * for their async templates to be resolved. + * + * + * ### Transclusion + * + * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and + * copying them to another part of the DOM, while maintaining their connection to the original AngularJS + * scope from where they were taken. + * + * Transclusion is used (often with {@link ngTransclude}) to insert the + * original contents of a directive's element into a specified place in the template of the directive. + * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded + * content has access to the properties on the scope from which it was taken, even if the directive + * has isolated scope. + * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}. + * + * This makes it possible for the widget to have private state for its template, while the transcluded + * content has access to its originating scope. + * + *
    + * **Note:** When testing an element transclude directive you must not place the directive at the root of the + * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives + * Testing Transclusion Directives}. + *
    + * + * There are three kinds of transclusion depending upon whether you want to transclude just the contents of the + * directive's element, the entire element or multiple parts of the element contents: + * + * * `true` - transclude the content (i.e. the child nodes) of the directive's element. + * * `'element'` - transclude the whole of the directive's element including any directives on this + * element that defined at a lower priority than this directive. When used, the `template` + * property is ignored. + * * **`{...}` (an object hash):** - map elements of the content onto transclusion "slots" in the template. + * + * **Mult-slot transclusion** is declared by providing an object for the `transclude` property. + * + * This object is a map where the keys are the name of the slot to fill and the value is an element selector + * used to match the HTML to the slot. The element selector should be in normalized form (e.g. `myElement`) + * and will match the standard element variants (e.g. `my-element`, `my:element`, `data-my-element`, etc). + * + * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives} + * + * If the element selector is prefixed with a `?` then that slot is optional. + * + * For example, the transclude object `{ slotA: '?myCustomElement' }` maps `` elements to + * the `slotA` slot, which can be accessed via the `$transclude` function or via the {@link ngTransclude} directive. + * + * Slots that are not marked as optional (`?`) will trigger a compile time error if there are no matching elements + * in the transclude content. If you wish to know if an optional slot was filled with content, then you can call + * `$transclude.isSlotFilled(slotName)` on the transclude function passed to the directive's link function and + * injectable into the directive's controller. + * + * + * #### Transclusion Functions + * + * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion + * function** to the directive's `link` function and `controller`. This transclusion function is a special + * **linking function** that will return the compiled contents linked to a new transclusion scope. + * + *
    + * If you are just using {@link ngTransclude} then you don't need to worry about this function, since + * ngTransclude will deal with it for us. + *
    + * + * If you want to manually control the insertion and removal of the transcluded content in your directive + * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery + * object that contains the compiled DOM, which is linked to the correct transclusion scope. + * + * When you call a transclusion function you can pass in a **clone attach function**. This function accepts + * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded + * content and the `scope` is the newly created transclusion scope, to which the clone is bound. + * + *
    + * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a transclude function + * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope. + *
    + * + * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone + * attach function**: + * + * ```js + * var transcludedContent, transclusionScope; + * + * $transclude(function(clone, scope) { + * element.append(clone); + * transcludedContent = clone; + * transclusionScope = scope; + * }); + * ``` + * + * Later, if you want to remove the transcluded content from your DOM then you should also destroy the + * associated transclusion scope: + * + * ```js + * transcludedContent.remove(); + * transclusionScope.$destroy(); + * ``` + * + *
    + * **Best Practice**: if you intend to add and remove transcluded content manually in your directive + * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it), + * then you are also responsible for calling `$destroy` on the transclusion scope. + *
    + * + * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat} + * automatically destroy their transcluded clones as necessary so you do not need to worry about this if + * you are simply using {@link ngTransclude} to inject the transclusion into your directive. + * + * + * #### Transclusion Scopes + * + * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion + * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed + * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it + * was taken. + * + * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look + * like this: + * + * ```html + *
    + *
    + *
    + *
    + *
    + *
    + * ``` + * + * The `$parent` scope hierarchy will look like this: + * + ``` + - $rootScope + - isolate + - transclusion + ``` + * + * but the scopes will inherit prototypically from different scopes to their `$parent`. + * + ``` + - $rootScope + - transclusion + - isolate + ``` + * + * + * ### Attributes + * + * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the + * `link()` or `compile()` functions. It has a variety of uses. + * + * * *Accessing normalized attribute names:* Directives like 'ngBind' can be expressed in many ways: + * 'ng:bind', `data-ng-bind`, or 'x-ng-bind'. The attributes object allows for normalized access + * to the attributes. + * + * * *Directive inter-communication:* All directives share the same instance of the attributes + * object which allows the directives to use the attributes object as inter directive + * communication. + * + * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object + * allowing other directives to read the interpolated value. + * + * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes + * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also + * the only way to easily get the actual value because during the linking phase the interpolation + * hasn't been evaluated yet and so the value is at this time set to `undefined`. + * + * ```js + * function linkingFn(scope, elm, attrs, ctrl) { + * // get the attribute value + * console.log(attrs.ngModel); + * + * // change the attribute + * attrs.$set('ngModel', 'new value'); + * + * // observe changes to interpolated attribute + * attrs.$observe('ngModel', function(value) { + * console.log('ngModel has changed value to ' + value); + * }); + * } + * ``` + * + * ## Example + * + *
    + * **Note**: Typically directives are registered with `module.directive`. The example below is + * to illustrate how `$compile` works. + *
    + * + + + +
    +
    +
    +
    +
    +
    + + it('should auto compile', function() { + var textarea = $('textarea'); + var output = $('div[compile]'); + // The initial state reads 'Hello Angular'. + expect(output.getText()).toBe('Hello Angular'); + textarea.clear(); + textarea.sendKeys('{{name}}!'); + expect(output.getText()).toBe('Angular!'); + }); + +
    + + * + * + * @param {string|DOMElement} element Element or HTML string to compile into a template function. + * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED. + * + *
    + * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it + * e.g. will not use the right outer scope. Please pass the transclude function as a + * `parentBoundTranscludeFn` to the link function instead. + *
    + * + * @param {number} maxPriority only apply directives lower than given priority (Only effects the + * root element(s), not their children) + * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template + * (a DOM element/tree) to a scope. Where: + * + * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to. + * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the + * `template` and call the `cloneAttachFn` function allowing the caller to attach the + * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is + * called as:
    `cloneAttachFn(clonedElement, scope)` where: + * + * * `clonedElement` - is a clone of the original `element` passed into the compiler. + * * `scope` - is the current scope with which the linking function is working with. + * + * * `options` - An optional object hash with linking options. If `options` is provided, then the following + * keys may be used to control linking behavior: + * + * * `parentBoundTranscludeFn` - the transclude function made available to + * directives; if given, it will be passed through to the link functions of + * directives found in `element` during compilation. + * * `transcludeControllers` - an object hash with keys that map controller names + * to a hash with the key `instance`, which maps to the controller instance; + * if given, it will make the controllers available to directives on the compileNode: + * ``` + * { + * parent: { + * instance: parentControllerInstance + * } + * } + * ``` + * * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add + * the cloned elements; only needed for transcludes that are allowed to contain non html + * elements (e.g. SVG elements). See also the directive.controller property. + * + * Calling the linking function returns the element of the template. It is either the original + * element passed in, or the clone of the element if the `cloneAttachFn` is provided. + * + * After linking the view is not updated until after a call to $digest which typically is done by + * Angular automatically. + * + * If you need access to the bound view, there are two ways to do it: + * + * - If you are not asking the linking function to clone the template, create the DOM element(s) + * before you send them to the compiler and keep this reference around. + * ```js + * var element = $compile('

    {{total}}

    ')(scope); + * ``` + * + * - if on the other hand, you need the element to be cloned, the view reference from the original + * example would not point to the clone, but rather to the original template that was cloned. In + * this case, you can access the clone via the cloneAttachFn: + * ```js + * var templateElement = angular.element('

    {{total}}

    '), + * scope = ....; + * + * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) { + * //attach the clone to DOM document at the right place + * }); + * + * //now we have reference to the cloned DOM via `clonedElement` + * ``` + * + * + * For information on how the compiler works, see the + * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide. + */ + +var $compileMinErr = minErr('$compile'); + +/** + * @ngdoc provider + * @name $compileProvider + * + * @description + */ +$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider']; +function $CompileProvider($provide, $$sanitizeUriProvider) { + var hasDirectives = {}, + Suffix = 'Directive', + COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/, + CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/, + ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'), + REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/; + + // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes + // The assumption is that future DOM event attribute names will begin with + // 'on' and be composed of only English letters. + var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/; + var bindingCache = createMap(); + + function parseIsolateBindings(scope, directiveName, isController) { + var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/; + + var bindings = {}; + + forEach(scope, function(definition, scopeName) { + if (definition in bindingCache) { + bindings[scopeName] = bindingCache[definition]; + return; + } + var match = definition.match(LOCAL_REGEXP); + + if (!match) { + throw $compileMinErr('iscp', + "Invalid {3} for directive '{0}'." + + " Definition: {... {1}: '{2}' ...}", + directiveName, scopeName, definition, + (isController ? "controller bindings definition" : + "isolate scope definition")); + } + + bindings[scopeName] = { + mode: match[1][0], + collection: match[2] === '*', + optional: match[3] === '?', + attrName: match[4] || scopeName + }; + if (match[4]) { + bindingCache[definition] = bindings[scopeName]; + } + }); + + return bindings; + } + + function parseDirectiveBindings(directive, directiveName) { + var bindings = { + isolateScope: null, + bindToController: null + }; + if (isObject(directive.scope)) { + if (directive.bindToController === true) { + bindings.bindToController = parseIsolateBindings(directive.scope, + directiveName, true); + bindings.isolateScope = {}; + } else { + bindings.isolateScope = parseIsolateBindings(directive.scope, + directiveName, false); + } + } + if (isObject(directive.bindToController)) { + bindings.bindToController = + parseIsolateBindings(directive.bindToController, directiveName, true); + } + if (isObject(bindings.bindToController)) { + var controller = directive.controller; + var controllerAs = directive.controllerAs; + if (!controller) { + // There is no controller, there may or may not be a controllerAs property + throw $compileMinErr('noctrl', + "Cannot bind to controller without directive '{0}'s controller.", + directiveName); + } else if (!identifierForController(controller, controllerAs)) { + // There is a controller, but no identifier or controllerAs property + throw $compileMinErr('noident', + "Cannot bind to controller without identifier for directive '{0}'.", + directiveName); + } + } + return bindings; + } + + function assertValidDirectiveName(name) { + var letter = name.charAt(0); + if (!letter || letter !== lowercase(letter)) { + throw $compileMinErr('baddir', "Directive/Component name '{0}' is invalid. The first character must be a lowercase letter", name); + } + if (name !== name.trim()) { + throw $compileMinErr('baddir', + "Directive/Component name '{0}' is invalid. The name should not contain leading or trailing whitespaces", + name); + } + } + + /** + * @ngdoc method + * @name $compileProvider#directive + * @kind function + * + * @description + * Register a new directive with the compiler. + * + * @param {string|Object} name Name of the directive in camel-case (i.e. ngBind which + * will match as ng-bind), or an object map of directives where the keys are the + * names and the values are the factories. + * @param {Function|Array} directiveFactory An injectable directive factory function. See the + * {@link guide/directive directive guide} and the {@link $compile compile API} for more info. + * @returns {ng.$compileProvider} Self for chaining. + */ + this.directive = function registerDirective(name, directiveFactory) { + assertNotHasOwnProperty(name, 'directive'); + if (isString(name)) { + assertValidDirectiveName(name); + assertArg(directiveFactory, 'directiveFactory'); + if (!hasDirectives.hasOwnProperty(name)) { + hasDirectives[name] = []; + $provide.factory(name + Suffix, ['$injector', '$exceptionHandler', + function($injector, $exceptionHandler) { + var directives = []; + forEach(hasDirectives[name], function(directiveFactory, index) { + try { + var directive = $injector.invoke(directiveFactory); + if (isFunction(directive)) { + directive = { compile: valueFn(directive) }; + } else if (!directive.compile && directive.link) { + directive.compile = valueFn(directive.link); + } + directive.priority = directive.priority || 0; + directive.index = index; + directive.name = directive.name || name; + directive.require = directive.require || (directive.controller && directive.name); + directive.restrict = directive.restrict || 'EA'; + directive.$$moduleName = directiveFactory.$$moduleName; + directives.push(directive); + } catch (e) { + $exceptionHandler(e); + } + }); + return directives; + }]); + } + hasDirectives[name].push(directiveFactory); + } else { + forEach(name, reverseParams(registerDirective)); + } + return this; + }; + + /** + * @ngdoc method + * @name $compileProvider#component + * @module ng + * @param {string} name Name of the component in camelCase (i.e. `myComp` which will match ``) + * @param {Object} options Component definition object (a simplified + * {@link ng.$compile#directive-definition-object directive definition object}), + * with the following properties (all optional): + * + * - `controller` – `{(string|function()=}` – controller constructor function that should be + * associated with newly created scope or the name of a {@link ng.$compile#-controller- + * registered controller} if passed as a string. An empty `noop` function by default. + * - `controllerAs` – `{string=}` – identifier name for to reference the controller in the component's scope. + * If present, the controller will be published to scope under the `controllerAs` name. + * If not present, this will default to be `$ctrl`. + * - `template` – `{string=|function()=}` – html template as a string or a function that + * returns an html template as a string which should be used as the contents of this component. + * Empty string by default. + * + * If `template` is a function, then it is {@link auto.$injector#invoke injected} with + * the following locals: + * + * - `$element` - Current element + * - `$attrs` - Current attributes object for the element + * + * - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html + * template that should be used as the contents of this component. + * + * If `templateUrl` is a function, then it is {@link auto.$injector#invoke injected} with + * the following locals: + * + * - `$element` - Current element + * - `$attrs` - Current attributes object for the element + * + * - `bindings` – `{object=}` – defines bindings between DOM attributes and component properties. + * Component properties are always bound to the component controller and not to the scope. + * See {@link ng.$compile#-bindtocontroller- `bindToController`}. + * - `transclude` – `{boolean=}` – whether {@link $compile#transclusion content transclusion} is enabled. + * Disabled by default. + * - `$...` – additional properties to attach to the directive factory function and the controller + * constructor function. (This is used by the component router to annotate) + * + * @returns {ng.$compileProvider} the compile provider itself, for chaining of function calls. + * @description + * Register a **component definition** with the compiler. This is a shorthand for registering a special + * type of directive, which represents a self-contained UI component in your application. Such components + * are always isolated (i.e. `scope: {}`) and are always restricted to elements (i.e. `restrict: 'E'`). + * + * Component definitions are very simple and do not require as much configuration as defining general + * directives. Component definitions usually consist only of a template and a controller backing it. + * + * In order to make the definition easier, components enforce best practices like use of `controllerAs`, + * `bindToController`. They always have **isolate scope** and are restricted to elements. + * + * Here are a few examples of how you would usually define components: + * + * ```js + * var myMod = angular.module(...); + * myMod.component('myComp', { + * template: '
    My name is {{$ctrl.name}}
    ', + * controller: function() { + * this.name = 'shahar'; + * } + * }); + * + * myMod.component('myComp', { + * template: '
    My name is {{$ctrl.name}}
    ', + * bindings: {name: '@'} + * }); + * + * myMod.component('myComp', { + * templateUrl: 'views/my-comp.html', + * controller: 'MyCtrl', + * controllerAs: 'ctrl', + * bindings: {name: '@'} + * }); + * + * ``` + * For more examples, and an in-depth guide, see the {@link guide/component component guide}. + * + *
    + * See also {@link ng.$compileProvider#directive $compileProvider.directive()}. + */ + this.component = function registerComponent(name, options) { + var controller = options.controller || noop; + + function factory($injector) { + function makeInjectable(fn) { + if (isFunction(fn) || isArray(fn)) { + return function(tElement, tAttrs) { + return $injector.invoke(fn, this, {$element: tElement, $attrs: tAttrs}); + }; + } else { + return fn; + } + } + + var template = (!options.template && !options.templateUrl ? '' : options.template); + return { + controller: controller, + controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl', + template: makeInjectable(template), + templateUrl: makeInjectable(options.templateUrl), + transclude: options.transclude, + scope: {}, + bindToController: options.bindings || {}, + restrict: 'E', + require: options.require + }; + } + + // Copy any annotation properties (starting with $) over to the factory function + // These could be used by libraries such as the new component router + forEach(options, function(val, key) { + if (key.charAt(0) === '$') { + factory[key] = val; + controller[key] = val; + } + }); + + factory.$inject = ['$injector']; + + return this.directive(name, factory); + }; + + + /** + * @ngdoc method + * @name $compileProvider#aHrefSanitizationWhitelist + * @kind function + * + * @description + * Retrieves or overrides the default regular expression that is used for whitelisting of safe + * urls during a[href] sanitization. + * + * The sanitization is a security measure aimed at preventing XSS attacks via html links. + * + * Any url about to be assigned to a[href] via data-binding is first normalized and turned into + * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist` + * regular expression. If a match is found, the original url is written into the dom. Otherwise, + * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. + * + * @param {RegExp=} regexp New regexp to whitelist urls with. + * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for + * chaining otherwise. + */ + this.aHrefSanitizationWhitelist = function(regexp) { + if (isDefined(regexp)) { + $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp); + return this; + } else { + return $$sanitizeUriProvider.aHrefSanitizationWhitelist(); + } + }; + + + /** + * @ngdoc method + * @name $compileProvider#imgSrcSanitizationWhitelist + * @kind function + * + * @description + * Retrieves or overrides the default regular expression that is used for whitelisting of safe + * urls during img[src] sanitization. + * + * The sanitization is a security measure aimed at prevent XSS attacks via html links. + * + * Any url about to be assigned to img[src] via data-binding is first normalized and turned into + * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist` + * regular expression. If a match is found, the original url is written into the dom. Otherwise, + * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. + * + * @param {RegExp=} regexp New regexp to whitelist urls with. + * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for + * chaining otherwise. + */ + this.imgSrcSanitizationWhitelist = function(regexp) { + if (isDefined(regexp)) { + $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp); + return this; + } else { + return $$sanitizeUriProvider.imgSrcSanitizationWhitelist(); + } + }; + + /** + * @ngdoc method + * @name $compileProvider#debugInfoEnabled + * + * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the + * current debugInfoEnabled state + * @returns {*} current value if used as getter or itself (chaining) if used as setter + * + * @kind function + * + * @description + * Call this method to enable/disable various debug runtime information in the compiler such as adding + * binding information and a reference to the current scope on to DOM elements. + * If enabled, the compiler will add the following to DOM elements that have been bound to the scope + * * `ng-binding` CSS class + * * `$binding` data property containing an array of the binding expressions + * + * You may want to disable this in production for a significant performance boost. See + * {@link guide/production#disabling-debug-data Disabling Debug Data} for more. + * + * The default value is true. + */ + var debugInfoEnabled = true; + this.debugInfoEnabled = function(enabled) { + if (isDefined(enabled)) { + debugInfoEnabled = enabled; + return this; + } + return debugInfoEnabled; + }; + + + var TTL = 10; + /** + * @ngdoc method + * @name $compileProvider#onChangesTtl + * @description + * + * Sets the number of times `$onChanges` hooks can trigger new changes before giving up and + * assuming that the model is unstable. + * + * The current default is 10 iterations. + * + * In complex applications it's possible that dependencies between `$onChanges` hooks and bindings will result + * in several iterations of calls to these hooks. However if an application needs more than the default 10 + * iterations to stabilize then you should investigate what is causing the model to continuously change during + * the `$onChanges` hook execution. + * + * Increasing the TTL could have performance implications, so you should not change it without proper justification. + * + * @param {number} limit The number of `$onChanges` hook iterations. + * @returns {number|object} the current limit (or `this` if called as a setter for chaining) + */ + this.onChangesTtl = function(value) { + if (arguments.length) { + TTL = value; + return this; + } + return TTL; + }; + + this.$get = [ + '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse', + '$controller', '$rootScope', '$sce', '$animate', '$$sanitizeUri', + function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse, + $controller, $rootScope, $sce, $animate, $$sanitizeUri) { + + var SIMPLE_ATTR_NAME = /^\w/; + var specialAttrHolder = document.createElement('div'); + + + + var onChangesTtl = TTL; + // The onChanges hooks should all be run together in a single digest + // When changes occur, the call to trigger their hooks will be added to this queue + var onChangesQueue; + + // This function is called in a $$postDigest to trigger all the onChanges hooks in a single digest + function flushOnChangesQueue() { + try { + if (!(--onChangesTtl)) { + // We have hit the TTL limit so reset everything + onChangesQueue = undefined; + throw $compileMinErr('infchng', '{0} $onChanges() iterations reached. Aborting!\n', TTL); + } + // We must run this hook in an apply since the $$postDigest runs outside apply + $rootScope.$apply(function() { + for (var i = 0, ii = onChangesQueue.length; i < ii; ++i) { + onChangesQueue[i](); + } + // Reset the queue to trigger a new schedule next time there is a change + onChangesQueue = undefined; + }); + } finally { + onChangesTtl++; + } + } + + + function Attributes(element, attributesToCopy) { + if (attributesToCopy) { + var keys = Object.keys(attributesToCopy); + var i, l, key; + + for (i = 0, l = keys.length; i < l; i++) { + key = keys[i]; + this[key] = attributesToCopy[key]; + } + } else { + this.$attr = {}; + } + + this.$$element = element; + } + + Attributes.prototype = { + /** + * @ngdoc method + * @name $compile.directive.Attributes#$normalize + * @kind function + * + * @description + * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or + * `data-`) to its normalized, camelCase form. + * + * Also there is special case for Moz prefix starting with upper case letter. + * + * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives} + * + * @param {string} name Name to normalize + */ + $normalize: directiveNormalize, + + + /** + * @ngdoc method + * @name $compile.directive.Attributes#$addClass + * @kind function + * + * @description + * Adds the CSS class value specified by the classVal parameter to the element. If animations + * are enabled then an animation will be triggered for the class addition. + * + * @param {string} classVal The className value that will be added to the element + */ + $addClass: function(classVal) { + if (classVal && classVal.length > 0) { + $animate.addClass(this.$$element, classVal); + } + }, + + /** + * @ngdoc method + * @name $compile.directive.Attributes#$removeClass + * @kind function + * + * @description + * Removes the CSS class value specified by the classVal parameter from the element. If + * animations are enabled then an animation will be triggered for the class removal. + * + * @param {string} classVal The className value that will be removed from the element + */ + $removeClass: function(classVal) { + if (classVal && classVal.length > 0) { + $animate.removeClass(this.$$element, classVal); + } + }, + + /** + * @ngdoc method + * @name $compile.directive.Attributes#$updateClass + * @kind function + * + * @description + * Adds and removes the appropriate CSS class values to the element based on the difference + * between the new and old CSS class values (specified as newClasses and oldClasses). + * + * @param {string} newClasses The current CSS className value + * @param {string} oldClasses The former CSS className value + */ + $updateClass: function(newClasses, oldClasses) { + var toAdd = tokenDifference(newClasses, oldClasses); + if (toAdd && toAdd.length) { + $animate.addClass(this.$$element, toAdd); + } + + var toRemove = tokenDifference(oldClasses, newClasses); + if (toRemove && toRemove.length) { + $animate.removeClass(this.$$element, toRemove); + } + }, + + /** + * Set a normalized attribute on the element in a way such that all directives + * can share the attribute. This function properly handles boolean attributes. + * @param {string} key Normalized key. (ie ngAttribute) + * @param {string|boolean} value The value to set. If `null` attribute will be deleted. + * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute. + * Defaults to true. + * @param {string=} attrName Optional none normalized name. Defaults to key. + */ + $set: function(key, value, writeAttr, attrName) { + // TODO: decide whether or not to throw an error if "class" + //is set through this function since it may cause $updateClass to + //become unstable. + + var node = this.$$element[0], + booleanKey = getBooleanAttrName(node, key), + aliasedKey = getAliasedAttrName(key), + observer = key, + nodeName; + + if (booleanKey) { + this.$$element.prop(key, value); + attrName = booleanKey; + } else if (aliasedKey) { + this[aliasedKey] = value; + observer = aliasedKey; + } + + this[key] = value; + + // translate normalized key to actual key + if (attrName) { + this.$attr[key] = attrName; + } else { + attrName = this.$attr[key]; + if (!attrName) { + this.$attr[key] = attrName = snake_case(key, '-'); + } + } + + nodeName = nodeName_(this.$$element); + + if ((nodeName === 'a' && (key === 'href' || key === 'xlinkHref')) || + (nodeName === 'img' && key === 'src')) { + // sanitize a[href] and img[src] values + this[key] = value = $$sanitizeUri(value, key === 'src'); + } else if (nodeName === 'img' && key === 'srcset') { + // sanitize img[srcset] values + var result = ""; + + // first check if there are spaces because it's not the same pattern + var trimmedSrcset = trim(value); + // ( 999x ,| 999w ,| ,|, ) + var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/; + var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/; + + // split srcset into tuple of uri and descriptor except for the last item + var rawUris = trimmedSrcset.split(pattern); + + // for each tuples + var nbrUrisWith2parts = Math.floor(rawUris.length / 2); + for (var i = 0; i < nbrUrisWith2parts; i++) { + var innerIdx = i * 2; + // sanitize the uri + result += $$sanitizeUri(trim(rawUris[innerIdx]), true); + // add the descriptor + result += (" " + trim(rawUris[innerIdx + 1])); + } + + // split the last item into uri and descriptor + var lastTuple = trim(rawUris[i * 2]).split(/\s/); + + // sanitize the last uri + result += $$sanitizeUri(trim(lastTuple[0]), true); + + // and add the last descriptor if any + if (lastTuple.length === 2) { + result += (" " + trim(lastTuple[1])); + } + this[key] = value = result; + } + + if (writeAttr !== false) { + if (value === null || isUndefined(value)) { + this.$$element.removeAttr(attrName); + } else { + if (SIMPLE_ATTR_NAME.test(attrName)) { + this.$$element.attr(attrName, value); + } else { + setSpecialAttr(this.$$element[0], attrName, value); + } + } + } + + // fire observers + var $$observers = this.$$observers; + $$observers && forEach($$observers[observer], function(fn) { + try { + fn(value); + } catch (e) { + $exceptionHandler(e); + } + }); + }, + + + /** + * @ngdoc method + * @name $compile.directive.Attributes#$observe + * @kind function + * + * @description + * Observes an interpolated attribute. + * + * The observer function will be invoked once during the next `$digest` following + * compilation. The observer is then invoked whenever the interpolated value + * changes. + * + * @param {string} key Normalized key. (ie ngAttribute) . + * @param {function(interpolatedValue)} fn Function that will be called whenever + the interpolated value of the attribute changes. + * See the {@link guide/interpolation#how-text-and-attribute-bindings-work Interpolation + * guide} for more info. + * @returns {function()} Returns a deregistration function for this observer. + */ + $observe: function(key, fn) { + var attrs = this, + $$observers = (attrs.$$observers || (attrs.$$observers = createMap())), + listeners = ($$observers[key] || ($$observers[key] = [])); + + listeners.push(fn); + $rootScope.$evalAsync(function() { + if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) { + // no one registered attribute interpolation function, so lets call it manually + fn(attrs[key]); + } + }); + + return function() { + arrayRemove(listeners, fn); + }; + } + }; + + function setSpecialAttr(element, attrName, value) { + // Attributes names that do not start with letters (such as `(click)`) cannot be set using `setAttribute` + // so we have to jump through some hoops to get such an attribute + // https://github.com/angular/angular.js/pull/13318 + specialAttrHolder.innerHTML = ""; + var attributes = specialAttrHolder.firstChild.attributes; + var attribute = attributes[0]; + // We have to remove the attribute from its container element before we can add it to the destination element + attributes.removeNamedItem(attribute.name); + attribute.value = value; + element.attributes.setNamedItem(attribute); + } + + function safeAddClass($element, className) { + try { + $element.addClass(className); + } catch (e) { + // ignore, since it means that we are trying to set class on + // SVG element, where class name is read-only. + } + } + + + var startSymbol = $interpolate.startSymbol(), + endSymbol = $interpolate.endSymbol(), + denormalizeTemplate = (startSymbol == '{{' && endSymbol == '}}') + ? identity + : function denormalizeTemplate(template) { + return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol); + }, + NG_ATTR_BINDING = /^ngAttr[A-Z]/; + var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/; + + compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) { + var bindings = $element.data('$binding') || []; + + if (isArray(binding)) { + bindings = bindings.concat(binding); + } else { + bindings.push(binding); + } + + $element.data('$binding', bindings); + } : noop; + + compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) { + safeAddClass($element, 'ng-binding'); + } : noop; + + compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) { + var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope'; + $element.data(dataName, scope); + } : noop; + + compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) { + safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope'); + } : noop; + + compile.$$createComment = function(directiveName, comment) { + var content = ''; + if (debugInfoEnabled) { + content = ' ' + (directiveName || '') + ': ' + (comment || '') + ' '; + } + return document.createComment(content); + }; + + return compile; + + //================================ + + function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, + previousCompileContext) { + if (!($compileNodes instanceof jqLite)) { + // jquery always rewraps, whereas we need to preserve the original selector so that we can + // modify it. + $compileNodes = jqLite($compileNodes); + } + + var NOT_EMPTY = /\S+/; + + // We can not compile top level text elements since text nodes can be merged and we will + // not be able to attach scope data to them, so we will wrap them in + for (var i = 0, len = $compileNodes.length; i < len; i++) { + var domNode = $compileNodes[i]; + + if (domNode.nodeType === NODE_TYPE_TEXT && domNode.nodeValue.match(NOT_EMPTY) /* non-empty */) { + jqLiteWrapNode(domNode, $compileNodes[i] = document.createElement('span')); + } + } + + var compositeLinkFn = + compileNodes($compileNodes, transcludeFn, $compileNodes, + maxPriority, ignoreDirective, previousCompileContext); + compile.$$addScopeClass($compileNodes); + var namespace = null; + return function publicLinkFn(scope, cloneConnectFn, options) { + assertArg(scope, 'scope'); + + if (previousCompileContext && previousCompileContext.needsNewScope) { + // A parent directive did a replace and a directive on this element asked + // for transclusion, which caused us to lose a layer of element on which + // we could hold the new transclusion scope, so we will create it manually + // here. + scope = scope.$parent.$new(); + } + + options = options || {}; + var parentBoundTranscludeFn = options.parentBoundTranscludeFn, + transcludeControllers = options.transcludeControllers, + futureParentElement = options.futureParentElement; + + // When `parentBoundTranscludeFn` is passed, it is a + // `controllersBoundTransclude` function (it was previously passed + // as `transclude` to directive.link) so we must unwrap it to get + // its `boundTranscludeFn` + if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) { + parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude; + } + + if (!namespace) { + namespace = detectNamespaceForChildElements(futureParentElement); + } + var $linkNode; + if (namespace !== 'html') { + // When using a directive with replace:true and templateUrl the $compileNodes + // (or a child element inside of them) + // might change, so we need to recreate the namespace adapted compileNodes + // for call to the link function. + // Note: This will already clone the nodes... + $linkNode = jqLite( + wrapTemplate(namespace, jqLite('
    ').append($compileNodes).html()) + ); + } else if (cloneConnectFn) { + // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart + // and sometimes changes the structure of the DOM. + $linkNode = JQLitePrototype.clone.call($compileNodes); + } else { + $linkNode = $compileNodes; + } + + if (transcludeControllers) { + for (var controllerName in transcludeControllers) { + $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance); + } + } + + compile.$$addScopeInfo($linkNode, scope); + + if (cloneConnectFn) cloneConnectFn($linkNode, scope); + if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn); + return $linkNode; + }; + } + + function detectNamespaceForChildElements(parentElement) { + // TODO: Make this detect MathML as well... + var node = parentElement && parentElement[0]; + if (!node) { + return 'html'; + } else { + return nodeName_(node) !== 'foreignobject' && toString.call(node).match(/SVG/) ? 'svg' : 'html'; + } + } + + /** + * Compile function matches each node in nodeList against the directives. Once all directives + * for a particular node are collected their compile functions are executed. The compile + * functions return values - the linking functions - are combined into a composite linking + * function, which is the a linking function for the node. + * + * @param {NodeList} nodeList an array of nodes or NodeList to compile + * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the + * scope argument is auto-generated to the new child of the transcluded parent scope. + * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then + * the rootElement must be set the jqLite collection of the compile root. This is + * needed so that the jqLite collection items can be replaced with widgets. + * @param {number=} maxPriority Max directive priority. + * @returns {Function} A composite linking function of all of the matched directives or null. + */ + function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective, + previousCompileContext) { + var linkFns = [], + attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound; + + for (var i = 0; i < nodeList.length; i++) { + attrs = new Attributes(); + + // we must always refer to nodeList[i] since the nodes can be replaced underneath us. + directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined, + ignoreDirective); + + nodeLinkFn = (directives.length) + ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement, + null, [], [], previousCompileContext) + : null; + + if (nodeLinkFn && nodeLinkFn.scope) { + compile.$$addScopeClass(attrs.$$element); + } + + childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || + !(childNodes = nodeList[i].childNodes) || + !childNodes.length) + ? null + : compileNodes(childNodes, + nodeLinkFn ? ( + (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement) + && nodeLinkFn.transclude) : transcludeFn); + + if (nodeLinkFn || childLinkFn) { + linkFns.push(i, nodeLinkFn, childLinkFn); + linkFnFound = true; + nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn; + } + + //use the previous context only for the first element in the virtual group + previousCompileContext = null; + } + + // return a linking function if we have found anything, null otherwise + return linkFnFound ? compositeLinkFn : null; + + function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) { + var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn; + var stableNodeList; + + + if (nodeLinkFnFound) { + // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our + // offsets don't get screwed up + var nodeListLength = nodeList.length; + stableNodeList = new Array(nodeListLength); + + // create a sparse array by only copying the elements which have a linkFn + for (i = 0; i < linkFns.length; i+=3) { + idx = linkFns[i]; + stableNodeList[idx] = nodeList[idx]; + } + } else { + stableNodeList = nodeList; + } + + for (i = 0, ii = linkFns.length; i < ii;) { + node = stableNodeList[linkFns[i++]]; + nodeLinkFn = linkFns[i++]; + childLinkFn = linkFns[i++]; + + if (nodeLinkFn) { + if (nodeLinkFn.scope) { + childScope = scope.$new(); + compile.$$addScopeInfo(jqLite(node), childScope); + } else { + childScope = scope; + } + + if (nodeLinkFn.transcludeOnThisElement) { + childBoundTranscludeFn = createBoundTranscludeFn( + scope, nodeLinkFn.transclude, parentBoundTranscludeFn); + + } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) { + childBoundTranscludeFn = parentBoundTranscludeFn; + + } else if (!parentBoundTranscludeFn && transcludeFn) { + childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn); + + } else { + childBoundTranscludeFn = null; + } + + nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn); + + } else if (childLinkFn) { + childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn); + } + } + } + } + + function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) { + function boundTranscludeFn(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) { + + if (!transcludedScope) { + transcludedScope = scope.$new(false, containingScope); + transcludedScope.$$transcluded = true; + } + + return transcludeFn(transcludedScope, cloneFn, { + parentBoundTranscludeFn: previousBoundTranscludeFn, + transcludeControllers: controllers, + futureParentElement: futureParentElement + }); + } + + // We need to attach the transclusion slots onto the `boundTranscludeFn` + // so that they are available inside the `controllersBoundTransclude` function + var boundSlots = boundTranscludeFn.$$slots = createMap(); + for (var slotName in transcludeFn.$$slots) { + if (transcludeFn.$$slots[slotName]) { + boundSlots[slotName] = createBoundTranscludeFn(scope, transcludeFn.$$slots[slotName], previousBoundTranscludeFn); + } else { + boundSlots[slotName] = null; + } + } + + return boundTranscludeFn; + } + + /** + * Looks for directives on the given node and adds them to the directive collection which is + * sorted. + * + * @param node Node to search. + * @param directives An array to which the directives are added to. This array is sorted before + * the function returns. + * @param attrs The shared attrs object which is used to populate the normalized attributes. + * @param {number=} maxPriority Max directive priority. + */ + function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) { + var nodeType = node.nodeType, + attrsMap = attrs.$attr, + match, + className; + + switch (nodeType) { + case NODE_TYPE_ELEMENT: /* Element */ + // use the node name: + addDirective(directives, + directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective); + + // iterate over the attributes + for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes, + j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) { + var attrStartName = false; + var attrEndName = false; + + attr = nAttrs[j]; + name = attr.name; + value = trim(attr.value); + + // support ngAttr attribute binding + ngAttrName = directiveNormalize(name); + if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) { + name = name.replace(PREFIX_REGEXP, '') + .substr(8).replace(/_(.)/g, function(match, letter) { + return letter.toUpperCase(); + }); + } + + var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE); + if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) { + attrStartName = name; + attrEndName = name.substr(0, name.length - 5) + 'end'; + name = name.substr(0, name.length - 6); + } + + nName = directiveNormalize(name.toLowerCase()); + attrsMap[nName] = name; + if (isNgAttr || !attrs.hasOwnProperty(nName)) { + attrs[nName] = value; + if (getBooleanAttrName(node, nName)) { + attrs[nName] = true; // presence means true + } + } + addAttrInterpolateDirective(node, directives, value, nName, isNgAttr); + addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName, + attrEndName); + } + + // use class as directive + className = node.className; + if (isObject(className)) { + // Maybe SVGAnimatedString + className = className.animVal; + } + if (isString(className) && className !== '') { + while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) { + nName = directiveNormalize(match[2]); + if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) { + attrs[nName] = trim(match[3]); + } + className = className.substr(match.index + match[0].length); + } + } + break; + case NODE_TYPE_TEXT: /* Text Node */ + if (msie === 11) { + // Workaround for #11781 + while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) { + node.nodeValue = node.nodeValue + node.nextSibling.nodeValue; + node.parentNode.removeChild(node.nextSibling); + } + } + addTextInterpolateDirective(directives, node.nodeValue); + break; + case NODE_TYPE_COMMENT: /* Comment */ + try { + match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue); + if (match) { + nName = directiveNormalize(match[1]); + if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) { + attrs[nName] = trim(match[2]); + } + } + } catch (e) { + // turns out that under some circumstances IE9 throws errors when one attempts to read + // comment's node value. + // Just ignore it and continue. (Can't seem to reproduce in test case.) + } + break; + } + + directives.sort(byPriority); + return directives; + } + + /** + * Given a node with an directive-start it collects all of the siblings until it finds + * directive-end. + * @param node + * @param attrStart + * @param attrEnd + * @returns {*} + */ + function groupScan(node, attrStart, attrEnd) { + var nodes = []; + var depth = 0; + if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) { + do { + if (!node) { + throw $compileMinErr('uterdir', + "Unterminated attribute, found '{0}' but no matching '{1}' found.", + attrStart, attrEnd); + } + if (node.nodeType == NODE_TYPE_ELEMENT) { + if (node.hasAttribute(attrStart)) depth++; + if (node.hasAttribute(attrEnd)) depth--; + } + nodes.push(node); + node = node.nextSibling; + } while (depth > 0); + } else { + nodes.push(node); + } + + return jqLite(nodes); + } + + /** + * Wrapper for linking function which converts normal linking function into a grouped + * linking function. + * @param linkFn + * @param attrStart + * @param attrEnd + * @returns {Function} + */ + function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) { + return function groupedElementsLink(scope, element, attrs, controllers, transcludeFn) { + element = groupScan(element[0], attrStart, attrEnd); + return linkFn(scope, element, attrs, controllers, transcludeFn); + }; + } + + /** + * A function generator that is used to support both eager and lazy compilation + * linking function. + * @param eager + * @param $compileNodes + * @param transcludeFn + * @param maxPriority + * @param ignoreDirective + * @param previousCompileContext + * @returns {Function} + */ + function compilationGenerator(eager, $compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) { + var compiled; + + if (eager) { + return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext); + } + return function lazyCompilation() { + if (!compiled) { + compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext); + + // Null out all of these references in order to make them eligible for garbage collection + // since this is a potentially long lived closure + $compileNodes = transcludeFn = previousCompileContext = null; + } + return compiled.apply(this, arguments); + }; + } + + /** + * Once the directives have been collected, their compile functions are executed. This method + * is responsible for inlining directive templates as well as terminating the application + * of the directives if the terminal directive has been reached. + * + * @param {Array} directives Array of collected directives to execute their compile function. + * this needs to be pre-sorted by priority order. + * @param {Node} compileNode The raw DOM node to apply the compile functions to + * @param {Object} templateAttrs The shared attribute function + * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the + * scope argument is auto-generated to the new + * child of the transcluded parent scope. + * @param {JQLite} jqCollection If we are working on the root of the compile tree then this + * argument has the root jqLite array so that we can replace nodes + * on it. + * @param {Object=} originalReplaceDirective An optional directive that will be ignored when + * compiling the transclusion. + * @param {Array.} preLinkFns + * @param {Array.} postLinkFns + * @param {Object} previousCompileContext Context used for previous compilation of the current + * node + * @returns {Function} linkFn + */ + function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, + jqCollection, originalReplaceDirective, preLinkFns, postLinkFns, + previousCompileContext) { + previousCompileContext = previousCompileContext || {}; + + var terminalPriority = -Number.MAX_VALUE, + newScopeDirective = previousCompileContext.newScopeDirective, + controllerDirectives = previousCompileContext.controllerDirectives, + newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective, + templateDirective = previousCompileContext.templateDirective, + nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective, + hasTranscludeDirective = false, + hasTemplate = false, + hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective, + $compileNode = templateAttrs.$$element = jqLite(compileNode), + directive, + directiveName, + $template, + replaceDirective = originalReplaceDirective, + childTranscludeFn = transcludeFn, + linkFn, + didScanForMultipleTransclusion = false, + mightHaveMultipleTransclusionError = false, + directiveValue; + + // executes all directives on the current element + for (var i = 0, ii = directives.length; i < ii; i++) { + directive = directives[i]; + var attrStart = directive.$$start; + var attrEnd = directive.$$end; + + // collect multiblock sections + if (attrStart) { + $compileNode = groupScan(compileNode, attrStart, attrEnd); + } + $template = undefined; + + if (terminalPriority > directive.priority) { + break; // prevent further processing of directives + } + + if (directiveValue = directive.scope) { + + // skip the check for directives with async templates, we'll check the derived sync + // directive when the template arrives + if (!directive.templateUrl) { + if (isObject(directiveValue)) { + // This directive is trying to add an isolated scope. + // Check that there is no scope of any kind already + assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective, + directive, $compileNode); + newIsolateScopeDirective = directive; + } else { + // This directive is trying to add a child scope. + // Check that there is no isolated scope already + assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive, + $compileNode); + } + } + + newScopeDirective = newScopeDirective || directive; + } + + directiveName = directive.name; + + // If we encounter a condition that can result in transclusion on the directive, + // then scan ahead in the remaining directives for others that may cause a multiple + // transclusion error to be thrown during the compilation process. If a matching directive + // is found, then we know that when we encounter a transcluded directive, we need to eagerly + // compile the `transclude` function rather than doing it lazily in order to throw + // exceptions at the correct time + if (!didScanForMultipleTransclusion && ((directive.replace && (directive.templateUrl || directive.template)) + || (directive.transclude && !directive.$$tlb))) { + var candidateDirective; + + for (var scanningIndex = i + 1; candidateDirective = directives[scanningIndex++];) { + if ((candidateDirective.transclude && !candidateDirective.$$tlb) + || (candidateDirective.replace && (candidateDirective.templateUrl || candidateDirective.template))) { + mightHaveMultipleTransclusionError = true; + break; + } + } + + didScanForMultipleTransclusion = true; + } + + if (!directive.templateUrl && directive.controller) { + directiveValue = directive.controller; + controllerDirectives = controllerDirectives || createMap(); + assertNoDuplicate("'" + directiveName + "' controller", + controllerDirectives[directiveName], directive, $compileNode); + controllerDirectives[directiveName] = directive; + } + + if (directiveValue = directive.transclude) { + hasTranscludeDirective = true; + + // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion. + // This option should only be used by directives that know how to safely handle element transclusion, + // where the transcluded nodes are added or replaced after linking. + if (!directive.$$tlb) { + assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode); + nonTlbTranscludeDirective = directive; + } + + if (directiveValue == 'element') { + hasElementTranscludeDirective = true; + terminalPriority = directive.priority; + $template = $compileNode; + $compileNode = templateAttrs.$$element = + jqLite(compile.$$createComment(directiveName, templateAttrs[directiveName])); + compileNode = $compileNode[0]; + replaceWith(jqCollection, sliceArgs($template), compileNode); + + // Support: Chrome < 50 + // https://github.com/angular/angular.js/issues/14041 + + // In the versions of V8 prior to Chrome 50, the document fragment that is created + // in the `replaceWith` function is improperly garbage collected despite still + // being referenced by the `parentNode` property of all of the child nodes. By adding + // a reference to the fragment via a different property, we can avoid that incorrect + // behavior. + // TODO: remove this line after Chrome 50 has been released + $template[0].$$parentNode = $template[0].parentNode; + + childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, terminalPriority, + replaceDirective && replaceDirective.name, { + // Don't pass in: + // - controllerDirectives - otherwise we'll create duplicates controllers + // - newIsolateScopeDirective or templateDirective - combining templates with + // element transclusion doesn't make sense. + // + // We need only nonTlbTranscludeDirective so that we prevent putting transclusion + // on the same element more than once. + nonTlbTranscludeDirective: nonTlbTranscludeDirective + }); + } else { + + var slots = createMap(); + + $template = jqLite(jqLiteClone(compileNode)).contents(); + + if (isObject(directiveValue)) { + + // We have transclusion slots, + // collect them up, compile them and store their transclusion functions + $template = []; + + var slotMap = createMap(); + var filledSlots = createMap(); + + // Parse the element selectors + forEach(directiveValue, function(elementSelector, slotName) { + // If an element selector starts with a ? then it is optional + var optional = (elementSelector.charAt(0) === '?'); + elementSelector = optional ? elementSelector.substring(1) : elementSelector; + + slotMap[elementSelector] = slotName; + + // We explicitly assign `null` since this implies that a slot was defined but not filled. + // Later when calling boundTransclusion functions with a slot name we only error if the + // slot is `undefined` + slots[slotName] = null; + + // filledSlots contains `true` for all slots that are either optional or have been + // filled. This is used to check that we have not missed any required slots + filledSlots[slotName] = optional; + }); + + // Add the matching elements into their slot + forEach($compileNode.contents(), function(node) { + var slotName = slotMap[directiveNormalize(nodeName_(node))]; + if (slotName) { + filledSlots[slotName] = true; + slots[slotName] = slots[slotName] || []; + slots[slotName].push(node); + } else { + $template.push(node); + } + }); + + // Check for required slots that were not filled + forEach(filledSlots, function(filled, slotName) { + if (!filled) { + throw $compileMinErr('reqslot', 'Required transclusion slot `{0}` was not filled.', slotName); + } + }); + + for (var slotName in slots) { + if (slots[slotName]) { + // Only define a transclusion function if the slot was filled + slots[slotName] = compilationGenerator(mightHaveMultipleTransclusionError, slots[slotName], transcludeFn); + } + } + } + + $compileNode.empty(); // clear contents + childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, undefined, + undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope}); + childTranscludeFn.$$slots = slots; + } + } + + if (directive.template) { + hasTemplate = true; + assertNoDuplicate('template', templateDirective, directive, $compileNode); + templateDirective = directive; + + directiveValue = (isFunction(directive.template)) + ? directive.template($compileNode, templateAttrs) + : directive.template; + + directiveValue = denormalizeTemplate(directiveValue); + + if (directive.replace) { + replaceDirective = directive; + if (jqLiteIsTextNode(directiveValue)) { + $template = []; + } else { + $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue))); + } + compileNode = $template[0]; + + if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) { + throw $compileMinErr('tplrt', + "Template for directive '{0}' must have exactly one root element. {1}", + directiveName, ''); + } + + replaceWith(jqCollection, $compileNode, compileNode); + + var newTemplateAttrs = {$attr: {}}; + + // combine directives from the original node and from the template: + // - take the array of directives for this element + // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed) + // - collect directives from the template and sort them by priority + // - combine directives as: processed + template + unprocessed + var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs); + var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1)); + + if (newIsolateScopeDirective || newScopeDirective) { + // The original directive caused the current element to be replaced but this element + // also needs to have a new scope, so we need to tell the template directives + // that they would need to get their scope from further up, if they require transclusion + markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective); + } + directives = directives.concat(templateDirectives).concat(unprocessedDirectives); + mergeTemplateAttributes(templateAttrs, newTemplateAttrs); + + ii = directives.length; + } else { + $compileNode.html(directiveValue); + } + } + + if (directive.templateUrl) { + hasTemplate = true; + assertNoDuplicate('template', templateDirective, directive, $compileNode); + templateDirective = directive; + + if (directive.replace) { + replaceDirective = directive; + } + + nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode, + templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, { + controllerDirectives: controllerDirectives, + newScopeDirective: (newScopeDirective !== directive) && newScopeDirective, + newIsolateScopeDirective: newIsolateScopeDirective, + templateDirective: templateDirective, + nonTlbTranscludeDirective: nonTlbTranscludeDirective + }); + ii = directives.length; + } else if (directive.compile) { + try { + linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn); + if (isFunction(linkFn)) { + addLinkFns(null, linkFn, attrStart, attrEnd); + } else if (linkFn) { + addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd); + } + } catch (e) { + $exceptionHandler(e, startingTag($compileNode)); + } + } + + if (directive.terminal) { + nodeLinkFn.terminal = true; + terminalPriority = Math.max(terminalPriority, directive.priority); + } + + } + + nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true; + nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective; + nodeLinkFn.templateOnThisElement = hasTemplate; + nodeLinkFn.transclude = childTranscludeFn; + + previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective; + + // might be normal or delayed nodeLinkFn depending on if templateUrl is present + return nodeLinkFn; + + //////////////////// + + function addLinkFns(pre, post, attrStart, attrEnd) { + if (pre) { + if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd); + pre.require = directive.require; + pre.directiveName = directiveName; + if (newIsolateScopeDirective === directive || directive.$$isolateScope) { + pre = cloneAndAnnotateFn(pre, {isolateScope: true}); + } + preLinkFns.push(pre); + } + if (post) { + if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd); + post.require = directive.require; + post.directiveName = directiveName; + if (newIsolateScopeDirective === directive || directive.$$isolateScope) { + post = cloneAndAnnotateFn(post, {isolateScope: true}); + } + postLinkFns.push(post); + } + } + + function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) { + var i, ii, linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element, + attrs, removeScopeBindingWatches, removeControllerBindingWatches; + + if (compileNode === linkNode) { + attrs = templateAttrs; + $element = templateAttrs.$$element; + } else { + $element = jqLite(linkNode); + attrs = new Attributes($element, templateAttrs); + } + + controllerScope = scope; + if (newIsolateScopeDirective) { + isolateScope = scope.$new(true); + } else if (newScopeDirective) { + controllerScope = scope.$parent; + } + + if (boundTranscludeFn) { + // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn` + // is later passed as `parentBoundTranscludeFn` to `publicLinkFn` + transcludeFn = controllersBoundTransclude; + transcludeFn.$$boundTransclude = boundTranscludeFn; + // expose the slots on the `$transclude` function + transcludeFn.isSlotFilled = function(slotName) { + return !!boundTranscludeFn.$$slots[slotName]; + }; + } + + if (controllerDirectives) { + elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective); + } + + if (newIsolateScopeDirective) { + // Initialize isolate scope bindings for new isolate scope directive. + compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective || + templateDirective === newIsolateScopeDirective.$$originalDirective))); + compile.$$addScopeClass($element, true); + isolateScope.$$isolateBindings = + newIsolateScopeDirective.$$isolateBindings; + removeScopeBindingWatches = initializeDirectiveBindings(scope, attrs, isolateScope, + isolateScope.$$isolateBindings, + newIsolateScopeDirective); + if (removeScopeBindingWatches) { + isolateScope.$on('$destroy', removeScopeBindingWatches); + } + } + + // Initialize bindToController bindings + for (var name in elementControllers) { + var controllerDirective = controllerDirectives[name]; + var controller = elementControllers[name]; + var bindings = controllerDirective.$$bindings.bindToController; + + if (controller.identifier && bindings) { + removeControllerBindingWatches = + initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective); + } + + var controllerResult = controller(); + if (controllerResult !== controller.instance) { + // If the controller constructor has a return value, overwrite the instance + // from setupControllers + controller.instance = controllerResult; + $element.data('$' + controllerDirective.name + 'Controller', controllerResult); + removeControllerBindingWatches && removeControllerBindingWatches(); + removeControllerBindingWatches = + initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective); + } + } + + // Bind the required controllers to the controller, if `require` is an object and `bindToController` is truthy + forEach(controllerDirectives, function(controllerDirective, name) { + var require = controllerDirective.require; + if (controllerDirective.bindToController && !isArray(require) && isObject(require)) { + extend(elementControllers[name].instance, getControllers(name, require, $element, elementControllers)); + } + }); + + // Handle the init and destroy lifecycle hooks on all controllers that have them + forEach(elementControllers, function(controller) { + var controllerInstance = controller.instance; + if (isFunction(controllerInstance.$onInit)) { + controllerInstance.$onInit(); + } + if (isFunction(controllerInstance.$onDestroy)) { + controllerScope.$on('$destroy', function callOnDestroyHook() { + controllerInstance.$onDestroy(); + }); + } + }); + + // PRELINKING + for (i = 0, ii = preLinkFns.length; i < ii; i++) { + linkFn = preLinkFns[i]; + invokeLinkFn(linkFn, + linkFn.isolateScope ? isolateScope : scope, + $element, + attrs, + linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), + transcludeFn + ); + } + + // RECURSION + // We only pass the isolate scope, if the isolate directive has a template, + // otherwise the child elements do not belong to the isolate directive. + var scopeToChild = scope; + if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) { + scopeToChild = isolateScope; + } + childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn); + + // POSTLINKING + for (i = postLinkFns.length - 1; i >= 0; i--) { + linkFn = postLinkFns[i]; + invokeLinkFn(linkFn, + linkFn.isolateScope ? isolateScope : scope, + $element, + attrs, + linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), + transcludeFn + ); + } + + // Trigger $postLink lifecycle hooks + forEach(elementControllers, function(controller) { + var controllerInstance = controller.instance; + if (isFunction(controllerInstance.$postLink)) { + controllerInstance.$postLink(); + } + }); + + // This is the function that is injected as `$transclude`. + // Note: all arguments are optional! + function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement, slotName) { + var transcludeControllers; + // No scope passed in: + if (!isScope(scope)) { + slotName = futureParentElement; + futureParentElement = cloneAttachFn; + cloneAttachFn = scope; + scope = undefined; + } + + if (hasElementTranscludeDirective) { + transcludeControllers = elementControllers; + } + if (!futureParentElement) { + futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element; + } + if (slotName) { + // slotTranscludeFn can be one of three things: + // * a transclude function - a filled slot + // * `null` - an optional slot that was not filled + // * `undefined` - a slot that was not declared (i.e. invalid) + var slotTranscludeFn = boundTranscludeFn.$$slots[slotName]; + if (slotTranscludeFn) { + return slotTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild); + } else if (isUndefined(slotTranscludeFn)) { + throw $compileMinErr('noslot', + 'No parent directive that requires a transclusion with slot name "{0}". ' + + 'Element: {1}', + slotName, startingTag($element)); + } + } else { + return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild); + } + } + } + } + + function getControllers(directiveName, require, $element, elementControllers) { + var value; + + if (isString(require)) { + var match = require.match(REQUIRE_PREFIX_REGEXP); + var name = require.substring(match[0].length); + var inheritType = match[1] || match[3]; + var optional = match[2] === '?'; + + //If only parents then start at the parent element + if (inheritType === '^^') { + $element = $element.parent(); + //Otherwise attempt getting the controller from elementControllers in case + //the element is transcluded (and has no data) and to avoid .data if possible + } else { + value = elementControllers && elementControllers[name]; + value = value && value.instance; + } + + if (!value) { + var dataName = '$' + name + 'Controller'; + value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName); + } + + if (!value && !optional) { + throw $compileMinErr('ctreq', + "Controller '{0}', required by directive '{1}', can't be found!", + name, directiveName); + } + } else if (isArray(require)) { + value = []; + for (var i = 0, ii = require.length; i < ii; i++) { + value[i] = getControllers(directiveName, require[i], $element, elementControllers); + } + } else if (isObject(require)) { + value = {}; + forEach(require, function(controller, property) { + value[property] = getControllers(directiveName, controller, $element, elementControllers); + }); + } + + return value || null; + } + + function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective) { + var elementControllers = createMap(); + for (var controllerKey in controllerDirectives) { + var directive = controllerDirectives[controllerKey]; + var locals = { + $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope, + $element: $element, + $attrs: attrs, + $transclude: transcludeFn + }; + + var controller = directive.controller; + if (controller == '@') { + controller = attrs[directive.name]; + } + + var controllerInstance = $controller(controller, locals, true, directive.controllerAs); + + // For directives with element transclusion the element is a comment. + // In this case .data will not attach any data. + // Instead, we save the controllers for the element in a local hash and attach to .data + // later, once we have the actual element. + elementControllers[directive.name] = controllerInstance; + $element.data('$' + directive.name + 'Controller', controllerInstance.instance); + } + return elementControllers; + } + + // Depending upon the context in which a directive finds itself it might need to have a new isolated + // or child scope created. For instance: + // * if the directive has been pulled into a template because another directive with a higher priority + // asked for element transclusion + // * if the directive itself asks for transclusion but it is at the root of a template and the original + // element was replaced. See https://github.com/angular/angular.js/issues/12936 + function markDirectiveScope(directives, isolateScope, newScope) { + for (var j = 0, jj = directives.length; j < jj; j++) { + directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope}); + } + } + + /** + * looks up the directive and decorates it with exception handling and proper parameters. We + * call this the boundDirective. + * + * @param {string} name name of the directive to look up. + * @param {string} location The directive must be found in specific format. + * String containing any of theses characters: + * + * * `E`: element name + * * `A': attribute + * * `C`: class + * * `M`: comment + * @returns {boolean} true if directive was added. + */ + function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName, + endAttrName) { + if (name === ignoreDirective) return null; + var match = null; + if (hasDirectives.hasOwnProperty(name)) { + for (var directive, directives = $injector.get(name + Suffix), + i = 0, ii = directives.length; i < ii; i++) { + try { + directive = directives[i]; + if ((isUndefined(maxPriority) || maxPriority > directive.priority) && + directive.restrict.indexOf(location) != -1) { + if (startAttrName) { + directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName}); + } + if (!directive.$$bindings) { + var bindings = directive.$$bindings = + parseDirectiveBindings(directive, directive.name); + if (isObject(bindings.isolateScope)) { + directive.$$isolateBindings = bindings.isolateScope; + } + } + tDirectives.push(directive); + match = directive; + } + } catch (e) { $exceptionHandler(e); } + } + } + return match; + } + + + /** + * looks up the directive and returns true if it is a multi-element directive, + * and therefore requires DOM nodes between -start and -end markers to be grouped + * together. + * + * @param {string} name name of the directive to look up. + * @returns true if directive was registered as multi-element. + */ + function directiveIsMultiElement(name) { + if (hasDirectives.hasOwnProperty(name)) { + for (var directive, directives = $injector.get(name + Suffix), + i = 0, ii = directives.length; i < ii; i++) { + directive = directives[i]; + if (directive.multiElement) { + return true; + } + } + } + return false; + } + + /** + * When the element is replaced with HTML template then the new attributes + * on the template need to be merged with the existing attributes in the DOM. + * The desired effect is to have both of the attributes present. + * + * @param {object} dst destination attributes (original DOM) + * @param {object} src source attributes (from the directive template) + */ + function mergeTemplateAttributes(dst, src) { + var srcAttr = src.$attr, + dstAttr = dst.$attr, + $element = dst.$$element; + + // reapply the old attributes to the new element + forEach(dst, function(value, key) { + if (key.charAt(0) != '$') { + if (src[key] && src[key] !== value) { + value += (key === 'style' ? ';' : ' ') + src[key]; + } + dst.$set(key, value, true, srcAttr[key]); + } + }); + + // copy the new attributes on the old attrs object + forEach(src, function(value, key) { + if (key == 'class') { + safeAddClass($element, value); + dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value; + } else if (key == 'style') { + $element.attr('style', $element.attr('style') + ';' + value); + dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value; + // `dst` will never contain hasOwnProperty as DOM parser won't let it. + // You will get an "InvalidCharacterError: DOM Exception 5" error if you + // have an attribute like "has-own-property" or "data-has-own-property", etc. + } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) { + dst[key] = value; + dstAttr[key] = srcAttr[key]; + } + }); + } + + + function compileTemplateUrl(directives, $compileNode, tAttrs, + $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) { + var linkQueue = [], + afterTemplateNodeLinkFn, + afterTemplateChildLinkFn, + beforeTemplateCompileNode = $compileNode[0], + origAsyncDirective = directives.shift(), + derivedSyncDirective = inherit(origAsyncDirective, { + templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective + }), + templateUrl = (isFunction(origAsyncDirective.templateUrl)) + ? origAsyncDirective.templateUrl($compileNode, tAttrs) + : origAsyncDirective.templateUrl, + templateNamespace = origAsyncDirective.templateNamespace; + + $compileNode.empty(); + + $templateRequest(templateUrl) + .then(function(content) { + var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn; + + content = denormalizeTemplate(content); + + if (origAsyncDirective.replace) { + if (jqLiteIsTextNode(content)) { + $template = []; + } else { + $template = removeComments(wrapTemplate(templateNamespace, trim(content))); + } + compileNode = $template[0]; + + if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) { + throw $compileMinErr('tplrt', + "Template for directive '{0}' must have exactly one root element. {1}", + origAsyncDirective.name, templateUrl); + } + + tempTemplateAttrs = {$attr: {}}; + replaceWith($rootElement, $compileNode, compileNode); + var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs); + + if (isObject(origAsyncDirective.scope)) { + // the original directive that caused the template to be loaded async required + // an isolate scope + markDirectiveScope(templateDirectives, true); + } + directives = templateDirectives.concat(directives); + mergeTemplateAttributes(tAttrs, tempTemplateAttrs); + } else { + compileNode = beforeTemplateCompileNode; + $compileNode.html(content); + } + + directives.unshift(derivedSyncDirective); + + afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, + childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns, + previousCompileContext); + forEach($rootElement, function(node, i) { + if (node == compileNode) { + $rootElement[i] = $compileNode[0]; + } + }); + afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn); + + while (linkQueue.length) { + var scope = linkQueue.shift(), + beforeTemplateLinkNode = linkQueue.shift(), + linkRootElement = linkQueue.shift(), + boundTranscludeFn = linkQueue.shift(), + linkNode = $compileNode[0]; + + if (scope.$$destroyed) continue; + + if (beforeTemplateLinkNode !== beforeTemplateCompileNode) { + var oldClasses = beforeTemplateLinkNode.className; + + if (!(previousCompileContext.hasElementTranscludeDirective && + origAsyncDirective.replace)) { + // it was cloned therefore we have to clone as well. + linkNode = jqLiteClone(compileNode); + } + replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode); + + // Copy in CSS classes from original node + safeAddClass(jqLite(linkNode), oldClasses); + } + if (afterTemplateNodeLinkFn.transcludeOnThisElement) { + childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn); + } else { + childBoundTranscludeFn = boundTranscludeFn; + } + afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement, + childBoundTranscludeFn); + } + linkQueue = null; + }); + + return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) { + var childBoundTranscludeFn = boundTranscludeFn; + if (scope.$$destroyed) return; + if (linkQueue) { + linkQueue.push(scope, + node, + rootElement, + childBoundTranscludeFn); + } else { + if (afterTemplateNodeLinkFn.transcludeOnThisElement) { + childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn); + } + afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn); + } + }; + } + + + /** + * Sorting function for bound directives. + */ + function byPriority(a, b) { + var diff = b.priority - a.priority; + if (diff !== 0) return diff; + if (a.name !== b.name) return (a.name < b.name) ? -1 : 1; + return a.index - b.index; + } + + function assertNoDuplicate(what, previousDirective, directive, element) { + + function wrapModuleNameIfDefined(moduleName) { + return moduleName ? + (' (module: ' + moduleName + ')') : + ''; + } + + if (previousDirective) { + throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}', + previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName), + directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element)); + } + } + + + function addTextInterpolateDirective(directives, text) { + var interpolateFn = $interpolate(text, true); + if (interpolateFn) { + directives.push({ + priority: 0, + compile: function textInterpolateCompileFn(templateNode) { + var templateNodeParent = templateNode.parent(), + hasCompileParent = !!templateNodeParent.length; + + // When transcluding a template that has bindings in the root + // we don't have a parent and thus need to add the class during linking fn. + if (hasCompileParent) compile.$$addBindingClass(templateNodeParent); + + return function textInterpolateLinkFn(scope, node) { + var parent = node.parent(); + if (!hasCompileParent) compile.$$addBindingClass(parent); + compile.$$addBindingInfo(parent, interpolateFn.expressions); + scope.$watch(interpolateFn, function interpolateFnWatchAction(value) { + node[0].nodeValue = value; + }); + }; + } + }); + } + } + + + function wrapTemplate(type, template) { + type = lowercase(type || 'html'); + switch (type) { + case 'svg': + case 'math': + var wrapper = document.createElement('div'); + wrapper.innerHTML = '<' + type + '>' + template + ''; + return wrapper.childNodes[0].childNodes; + default: + return template; + } + } + + + function getTrustedContext(node, attrNormalizedName) { + if (attrNormalizedName == "srcdoc") { + return $sce.HTML; + } + var tag = nodeName_(node); + // maction[xlink:href] can source SVG. It's not limited to . + if (attrNormalizedName == "xlinkHref" || + (tag == "form" && attrNormalizedName == "action") || + (tag != "img" && (attrNormalizedName == "src" || + attrNormalizedName == "ngSrc"))) { + return $sce.RESOURCE_URL; + } + } + + + function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) { + var trustedContext = getTrustedContext(node, name); + allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing; + + var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing); + + // no interpolation found -> ignore + if (!interpolateFn) return; + + + if (name === "multiple" && nodeName_(node) === "select") { + throw $compileMinErr("selmulti", + "Binding to the 'multiple' attribute is not supported. Element: {0}", + startingTag(node)); + } + + directives.push({ + priority: 100, + compile: function() { + return { + pre: function attrInterpolatePreLinkFn(scope, element, attr) { + var $$observers = (attr.$$observers || (attr.$$observers = createMap())); + + if (EVENT_HANDLER_ATTR_REGEXP.test(name)) { + throw $compileMinErr('nodomevents', + "Interpolations for HTML DOM event attributes are disallowed. Please use the " + + "ng- versions (such as ng-click instead of onclick) instead."); + } + + // If the attribute has changed since last $interpolate()ed + var newValue = attr[name]; + if (newValue !== value) { + // we need to interpolate again since the attribute value has been updated + // (e.g. by another directive's compile function) + // ensure unset/empty values make interpolateFn falsy + interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing); + value = newValue; + } + + // if attribute was updated so that there is no interpolation going on we don't want to + // register any observers + if (!interpolateFn) return; + + // initialize attr object so that it's ready in case we need the value for isolate + // scope initialization, otherwise the value would not be available from isolate + // directive's linking fn during linking phase + attr[name] = interpolateFn(scope); + + ($$observers[name] || ($$observers[name] = [])).$$inter = true; + (attr.$$observers && attr.$$observers[name].$$scope || scope). + $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) { + //special case for class attribute addition + removal + //so that class changes can tap into the animation + //hooks provided by the $animate service. Be sure to + //skip animations when the first digest occurs (when + //both the new and the old values are the same) since + //the CSS classes are the non-interpolated values + if (name === 'class' && newValue != oldValue) { + attr.$updateClass(newValue, oldValue); + } else { + attr.$set(name, newValue); + } + }); + } + }; + } + }); + } + + + /** + * This is a special jqLite.replaceWith, which can replace items which + * have no parents, provided that the containing jqLite collection is provided. + * + * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes + * in the root of the tree. + * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep + * the shell, but replace its DOM node reference. + * @param {Node} newNode The new DOM node. + */ + function replaceWith($rootElement, elementsToRemove, newNode) { + var firstElementToRemove = elementsToRemove[0], + removeCount = elementsToRemove.length, + parent = firstElementToRemove.parentNode, + i, ii; + + if ($rootElement) { + for (i = 0, ii = $rootElement.length; i < ii; i++) { + if ($rootElement[i] == firstElementToRemove) { + $rootElement[i++] = newNode; + for (var j = i, j2 = j + removeCount - 1, + jj = $rootElement.length; + j < jj; j++, j2++) { + if (j2 < jj) { + $rootElement[j] = $rootElement[j2]; + } else { + delete $rootElement[j]; + } + } + $rootElement.length -= removeCount - 1; + + // If the replaced element is also the jQuery .context then replace it + // .context is a deprecated jQuery api, so we should set it only when jQuery set it + // http://api.jquery.com/context/ + if ($rootElement.context === firstElementToRemove) { + $rootElement.context = newNode; + } + break; + } + } + } + + if (parent) { + parent.replaceChild(newNode, firstElementToRemove); + } + + // Append all the `elementsToRemove` to a fragment. This will... + // - remove them from the DOM + // - allow them to still be traversed with .nextSibling + // - allow a single fragment.qSA to fetch all elements being removed + var fragment = document.createDocumentFragment(); + for (i = 0; i < removeCount; i++) { + fragment.appendChild(elementsToRemove[i]); + } + + if (jqLite.hasData(firstElementToRemove)) { + // Copy over user data (that includes Angular's $scope etc.). Don't copy private + // data here because there's no public interface in jQuery to do that and copying over + // event listeners (which is the main use of private data) wouldn't work anyway. + jqLite.data(newNode, jqLite.data(firstElementToRemove)); + + // Remove $destroy event listeners from `firstElementToRemove` + jqLite(firstElementToRemove).off('$destroy'); + } + + // Cleanup any data/listeners on the elements and children. + // This includes invoking the $destroy event on any elements with listeners. + jqLite.cleanData(fragment.querySelectorAll('*')); + + // Update the jqLite collection to only contain the `newNode` + for (i = 1; i < removeCount; i++) { + delete elementsToRemove[i]; + } + elementsToRemove[0] = newNode; + elementsToRemove.length = 1; + } + + + function cloneAndAnnotateFn(fn, annotation) { + return extend(function() { return fn.apply(null, arguments); }, fn, annotation); + } + + + function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) { + try { + linkFn(scope, $element, attrs, controllers, transcludeFn); + } catch (e) { + $exceptionHandler(e, startingTag($element)); + } + } + + + // Set up $watches for isolate scope and controller bindings. This process + // only occurs for isolate scopes and new scopes with controllerAs. + function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) { + var removeWatchCollection = []; + var changes; + forEach(bindings, function initializeBinding(definition, scopeName) { + var attrName = definition.attrName, + optional = definition.optional, + mode = definition.mode, // @, =, or & + lastValue, + parentGet, parentSet, compare, removeWatch; + + switch (mode) { + + case '@': + if (!optional && !hasOwnProperty.call(attrs, attrName)) { + destination[scopeName] = attrs[attrName] = void 0; + } + attrs.$observe(attrName, function(value) { + if (isString(value)) { + var oldValue = destination[scopeName]; + recordChanges(scopeName, value, oldValue); + destination[scopeName] = value; + } + }); + attrs.$$observers[attrName].$$scope = scope; + lastValue = attrs[attrName]; + if (isString(lastValue)) { + // If the attribute has been provided then we trigger an interpolation to ensure + // the value is there for use in the link fn + destination[scopeName] = $interpolate(lastValue)(scope); + } else if (isBoolean(lastValue)) { + // If the attributes is one of the BOOLEAN_ATTR then Angular will have converted + // the value to boolean rather than a string, so we special case this situation + destination[scopeName] = lastValue; + } + break; + + case '=': + if (!hasOwnProperty.call(attrs, attrName)) { + if (optional) break; + attrs[attrName] = void 0; + } + if (optional && !attrs[attrName]) break; + + parentGet = $parse(attrs[attrName]); + if (parentGet.literal) { + compare = equals; + } else { + compare = function simpleCompare(a, b) { return a === b || (a !== a && b !== b); }; + } + parentSet = parentGet.assign || function() { + // reset the change, or we will throw this exception on every $digest + lastValue = destination[scopeName] = parentGet(scope); + throw $compileMinErr('nonassign', + "Expression '{0}' in attribute '{1}' used with directive '{2}' is non-assignable!", + attrs[attrName], attrName, directive.name); + }; + lastValue = destination[scopeName] = parentGet(scope); + var parentValueWatch = function parentValueWatch(parentValue) { + if (!compare(parentValue, destination[scopeName])) { + // we are out of sync and need to copy + if (!compare(parentValue, lastValue)) { + // parent changed and it has precedence + destination[scopeName] = parentValue; + } else { + // if the parent can be assigned then do so + parentSet(scope, parentValue = destination[scopeName]); + } + } + return lastValue = parentValue; + }; + parentValueWatch.$stateful = true; + if (definition.collection) { + removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch); + } else { + removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal); + } + removeWatchCollection.push(removeWatch); + break; + + case '<': + if (!hasOwnProperty.call(attrs, attrName)) { + if (optional) break; + attrs[attrName] = void 0; + } + if (optional && !attrs[attrName]) break; + + parentGet = $parse(attrs[attrName]); + + destination[scopeName] = parentGet(scope); + + removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newParentValue) { + var oldValue = destination[scopeName]; + recordChanges(scopeName, newParentValue, oldValue); + destination[scopeName] = newParentValue; + }, parentGet.literal); + + removeWatchCollection.push(removeWatch); + break; + + case '&': + // Don't assign Object.prototype method to scope + parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop; + + // Don't assign noop to destination if expression is not valid + if (parentGet === noop && optional) break; + + destination[scopeName] = function(locals) { + return parentGet(scope, locals); + }; + break; + } + }); + + function recordChanges(key, currentValue, previousValue) { + if (isFunction(destination.$onChanges) && currentValue !== previousValue) { + // If we have not already scheduled the top level onChangesQueue handler then do so now + if (!onChangesQueue) { + scope.$$postDigest(flushOnChangesQueue); + onChangesQueue = []; + } + // If we have not already queued a trigger of onChanges for this controller then do so now + if (!changes) { + changes = {}; + onChangesQueue.push(triggerOnChangesHook); + } + // If the has been a change on this property already then we need to reuse the previous value + if (changes[key]) { + previousValue = changes[key].previousValue; + } + // Store this change + changes[key] = {previousValue: previousValue, currentValue: currentValue}; + } + } + + function triggerOnChangesHook() { + destination.$onChanges(changes); + // Now clear the changes so that we schedule onChanges when more changes arrive + changes = undefined; + } + + return removeWatchCollection.length && function removeWatches() { + for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) { + removeWatchCollection[i](); + } + }; + } + }]; +} + +var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i; +/** + * Converts all accepted directives format into proper directive name. + * @param name Name to normalize + */ +function directiveNormalize(name) { + return camelCase(name.replace(PREFIX_REGEXP, '')); +} + +/** + * @ngdoc type + * @name $compile.directive.Attributes + * + * @description + * A shared object between directive compile / linking functions which contains normalized DOM + * element attributes. The values reflect current binding state `{{ }}`. The normalization is + * needed since all of these are treated as equivalent in Angular: + * + * ``` + * + * ``` + */ + +/** + * @ngdoc property + * @name $compile.directive.Attributes#$attr + * + * @description + * A map of DOM element attribute names to the normalized name. This is + * needed to do reverse lookup from normalized name back to actual name. + */ + + +/** + * @ngdoc method + * @name $compile.directive.Attributes#$set + * @kind function + * + * @description + * Set DOM element attribute value. + * + * + * @param {string} name Normalized element attribute name of the property to modify. The name is + * reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr} + * property to the original name. + * @param {string} value Value to set the attribute to. The value can be an interpolated string. + */ + + + +/** + * Closure compiler type information + */ + +function nodesetLinkingFn( + /* angular.Scope */ scope, + /* NodeList */ nodeList, + /* Element */ rootElement, + /* function(Function) */ boundTranscludeFn +) {} + +function directiveLinkingFn( + /* nodesetLinkingFn */ nodesetLinkingFn, + /* angular.Scope */ scope, + /* Node */ node, + /* Element */ rootElement, + /* function(Function) */ boundTranscludeFn +) {} + +function tokenDifference(str1, str2) { + var values = '', + tokens1 = str1.split(/\s+/), + tokens2 = str2.split(/\s+/); + + outer: + for (var i = 0; i < tokens1.length; i++) { + var token = tokens1[i]; + for (var j = 0; j < tokens2.length; j++) { + if (token == tokens2[j]) continue outer; + } + values += (values.length > 0 ? ' ' : '') + token; + } + return values; +} + +function removeComments(jqNodes) { + jqNodes = jqLite(jqNodes); + var i = jqNodes.length; + + if (i <= 1) { + return jqNodes; + } + + while (i--) { + var node = jqNodes[i]; + if (node.nodeType === NODE_TYPE_COMMENT) { + splice.call(jqNodes, i, 1); + } + } + return jqNodes; +} + +var $controllerMinErr = minErr('$controller'); + + +var CNTRL_REG = /^(\S+)(\s+as\s+([\w$]+))?$/; +function identifierForController(controller, ident) { + if (ident && isString(ident)) return ident; + if (isString(controller)) { + var match = CNTRL_REG.exec(controller); + if (match) return match[3]; + } +} + + +/** + * @ngdoc provider + * @name $controllerProvider + * @description + * The {@link ng.$controller $controller service} is used by Angular to create new + * controllers. + * + * This provider allows controller registration via the + * {@link ng.$controllerProvider#register register} method. + */ +function $ControllerProvider() { + var controllers = {}, + globals = false; + + /** + * @ngdoc method + * @name $controllerProvider#has + * @param {string} name Controller name to check. + */ + this.has = function(name) { + return controllers.hasOwnProperty(name); + }; + + /** + * @ngdoc method + * @name $controllerProvider#register + * @param {string|Object} name Controller name, or an object map of controllers where the keys are + * the names and the values are the constructors. + * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI + * annotations in the array notation). + */ + this.register = function(name, constructor) { + assertNotHasOwnProperty(name, 'controller'); + if (isObject(name)) { + extend(controllers, name); + } else { + controllers[name] = constructor; + } + }; + + /** + * @ngdoc method + * @name $controllerProvider#allowGlobals + * @description If called, allows `$controller` to find controller constructors on `window` + */ + this.allowGlobals = function() { + globals = true; + }; + + + this.$get = ['$injector', '$window', function($injector, $window) { + + /** + * @ngdoc service + * @name $controller + * @requires $injector + * + * @param {Function|string} constructor If called with a function then it's considered to be the + * controller constructor function. Otherwise it's considered to be a string which is used + * to retrieve the controller constructor using the following steps: + * + * * check if a controller with given name is registered via `$controllerProvider` + * * check if evaluating the string on the current scope returns a constructor + * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global + * `window` object (not recommended) + * + * The string can use the `controller as property` syntax, where the controller instance is published + * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this + * to work correctly. + * + * @param {Object} locals Injection locals for Controller. + * @return {Object} Instance of given controller. + * + * @description + * `$controller` service is responsible for instantiating controllers. + * + * It's just a simple call to {@link auto.$injector $injector}, but extracted into + * a service, so that one can override this service with [BC version](https://gist.github.com/1649788). + */ + return function $controller(expression, locals, later, ident) { + // PRIVATE API: + // param `later` --- indicates that the controller's constructor is invoked at a later time. + // If true, $controller will allocate the object with the correct + // prototype chain, but will not invoke the controller until a returned + // callback is invoked. + // param `ident` --- An optional label which overrides the label parsed from the controller + // expression, if any. + var instance, match, constructor, identifier; + later = later === true; + if (ident && isString(ident)) { + identifier = ident; + } + + if (isString(expression)) { + match = expression.match(CNTRL_REG); + if (!match) { + throw $controllerMinErr('ctrlfmt', + "Badly formed controller string '{0}'. " + + "Must match `__name__ as __id__` or `__name__`.", expression); + } + constructor = match[1], + identifier = identifier || match[3]; + expression = controllers.hasOwnProperty(constructor) + ? controllers[constructor] + : getter(locals.$scope, constructor, true) || + (globals ? getter($window, constructor, true) : undefined); + + assertArgFn(expression, constructor, true); + } + + if (later) { + // Instantiate controller later: + // This machinery is used to create an instance of the object before calling the + // controller's constructor itself. + // + // This allows properties to be added to the controller before the constructor is + // invoked. Primarily, this is used for isolate scope bindings in $compile. + // + // This feature is not intended for use by applications, and is thus not documented + // publicly. + // Object creation: http://jsperf.com/create-constructor/2 + var controllerPrototype = (isArray(expression) ? + expression[expression.length - 1] : expression).prototype; + instance = Object.create(controllerPrototype || null); + + if (identifier) { + addIdentifier(locals, identifier, instance, constructor || expression.name); + } + + var instantiate; + return instantiate = extend(function $controllerInit() { + var result = $injector.invoke(expression, instance, locals, constructor); + if (result !== instance && (isObject(result) || isFunction(result))) { + instance = result; + if (identifier) { + // If result changed, re-assign controllerAs value to scope. + addIdentifier(locals, identifier, instance, constructor || expression.name); + } + } + return instance; + }, { + instance: instance, + identifier: identifier + }); + } + + instance = $injector.instantiate(expression, locals, constructor); + + if (identifier) { + addIdentifier(locals, identifier, instance, constructor || expression.name); + } + + return instance; + }; + + function addIdentifier(locals, identifier, instance, name) { + if (!(locals && isObject(locals.$scope))) { + throw minErr('$controller')('noscp', + "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.", + name, identifier); + } + + locals.$scope[identifier] = instance; + } + }]; +} + +/** + * @ngdoc service + * @name $document + * @requires $window + * + * @description + * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object. + * + * @example + + +
    +

    $document title:

    +

    window.document title:

    +
    +
    + + angular.module('documentExample', []) + .controller('ExampleController', ['$scope', '$document', function($scope, $document) { + $scope.title = $document[0].title; + $scope.windowTitle = angular.element(window.document)[0].title; + }]); + +
    + */ +function $DocumentProvider() { + this.$get = ['$window', function(window) { + return jqLite(window.document); + }]; +} + +/** + * @ngdoc service + * @name $exceptionHandler + * @requires ng.$log + * + * @description + * Any uncaught exception in angular expressions is delegated to this service. + * The default implementation simply delegates to `$log.error` which logs it into + * the browser console. + * + * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by + * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing. + * + * ## Example: + * + * ```js + * angular.module('exceptionOverride', []).factory('$exceptionHandler', function() { + * return function(exception, cause) { + * exception.message += ' (caused by "' + cause + '")'; + * throw exception; + * }; + * }); + * ``` + * + * This example will override the normal action of `$exceptionHandler`, to make angular + * exceptions fail hard when they happen, instead of just logging to the console. + * + *
    + * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind` + * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler} + * (unless executed during a digest). + * + * If you wish, you can manually delegate exceptions, e.g. + * `try { ... } catch(e) { $exceptionHandler(e); }` + * + * @param {Error} exception Exception associated with the error. + * @param {string=} cause optional information about the context in which + * the error was thrown. + * + */ +function $ExceptionHandlerProvider() { + this.$get = ['$log', function($log) { + return function(exception, cause) { + $log.error.apply($log, arguments); + }; + }]; +} + +var $$ForceReflowProvider = function() { + this.$get = ['$document', function($document) { + return function(domNode) { + //the line below will force the browser to perform a repaint so + //that all the animated elements within the animation frame will + //be properly updated and drawn on screen. This is required to + //ensure that the preparation animation is properly flushed so that + //the active state picks up from there. DO NOT REMOVE THIS LINE. + //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH + //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND + //WILL TAKE YEARS AWAY FROM YOUR LIFE. + if (domNode) { + if (!domNode.nodeType && domNode instanceof jqLite) { + domNode = domNode[0]; + } + } else { + domNode = $document[0].body; + } + return domNode.offsetWidth + 1; + }; + }]; +}; + +var APPLICATION_JSON = 'application/json'; +var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'}; +var JSON_START = /^\[|^\{(?!\{)/; +var JSON_ENDS = { + '[': /]$/, + '{': /}$/ +}; +var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/; +var $httpMinErr = minErr('$http'); +var $httpMinErrLegacyFn = function(method) { + return function() { + throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method); + }; +}; + +function serializeValue(v) { + if (isObject(v)) { + return isDate(v) ? v.toISOString() : toJson(v); + } + return v; +} + + +function $HttpParamSerializerProvider() { + /** + * @ngdoc service + * @name $httpParamSerializer + * @description + * + * Default {@link $http `$http`} params serializer that converts objects to strings + * according to the following rules: + * + * * `{'foo': 'bar'}` results in `foo=bar` + * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object) + * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element) + * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object) + * + * Note that serializer will sort the request parameters alphabetically. + * */ + + this.$get = function() { + return function ngParamSerializer(params) { + if (!params) return ''; + var parts = []; + forEachSorted(params, function(value, key) { + if (value === null || isUndefined(value)) return; + if (isArray(value)) { + forEach(value, function(v) { + parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v))); + }); + } else { + parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value))); + } + }); + + return parts.join('&'); + }; + }; +} + +function $HttpParamSerializerJQLikeProvider() { + /** + * @ngdoc service + * @name $httpParamSerializerJQLike + * @description + * + * Alternative {@link $http `$http`} params serializer that follows + * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic. + * The serializer will also sort the params alphabetically. + * + * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property: + * + * ```js + * $http({ + * url: myUrl, + * method: 'GET', + * params: myParams, + * paramSerializer: '$httpParamSerializerJQLike' + * }); + * ``` + * + * It is also possible to set it as the default `paramSerializer` in the + * {@link $httpProvider#defaults `$httpProvider`}. + * + * Additionally, you can inject the serializer and use it explicitly, for example to serialize + * form data for submission: + * + * ```js + * .controller(function($http, $httpParamSerializerJQLike) { + * //... + * + * $http({ + * url: myUrl, + * method: 'POST', + * data: $httpParamSerializerJQLike(myData), + * headers: { + * 'Content-Type': 'application/x-www-form-urlencoded' + * } + * }); + * + * }); + * ``` + * + * */ + this.$get = function() { + return function jQueryLikeParamSerializer(params) { + if (!params) return ''; + var parts = []; + serialize(params, '', true); + return parts.join('&'); + + function serialize(toSerialize, prefix, topLevel) { + if (toSerialize === null || isUndefined(toSerialize)) return; + if (isArray(toSerialize)) { + forEach(toSerialize, function(value, index) { + serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']'); + }); + } else if (isObject(toSerialize) && !isDate(toSerialize)) { + forEachSorted(toSerialize, function(value, key) { + serialize(value, prefix + + (topLevel ? '' : '[') + + key + + (topLevel ? '' : ']')); + }); + } else { + parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize))); + } + } + }; + }; +} + +function defaultHttpResponseTransform(data, headers) { + if (isString(data)) { + // Strip json vulnerability protection prefix and trim whitespace + var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim(); + + if (tempData) { + var contentType = headers('Content-Type'); + if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) { + data = fromJson(tempData); + } + } + } + + return data; +} + +function isJsonLike(str) { + var jsonStart = str.match(JSON_START); + return jsonStart && JSON_ENDS[jsonStart[0]].test(str); +} + +/** + * Parse headers into key value object + * + * @param {string} headers Raw headers as a string + * @returns {Object} Parsed headers as key value object + */ +function parseHeaders(headers) { + var parsed = createMap(), i; + + function fillInParsed(key, val) { + if (key) { + parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; + } + } + + if (isString(headers)) { + forEach(headers.split('\n'), function(line) { + i = line.indexOf(':'); + fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1))); + }); + } else if (isObject(headers)) { + forEach(headers, function(headerVal, headerKey) { + fillInParsed(lowercase(headerKey), trim(headerVal)); + }); + } + + return parsed; +} + + +/** + * Returns a function that provides access to parsed headers. + * + * Headers are lazy parsed when first requested. + * @see parseHeaders + * + * @param {(string|Object)} headers Headers to provide access to. + * @returns {function(string=)} Returns a getter function which if called with: + * + * - if called with single an argument returns a single header value or null + * - if called with no arguments returns an object containing all headers. + */ +function headersGetter(headers) { + var headersObj; + + return function(name) { + if (!headersObj) headersObj = parseHeaders(headers); + + if (name) { + var value = headersObj[lowercase(name)]; + if (value === void 0) { + value = null; + } + return value; + } + + return headersObj; + }; +} + + +/** + * Chain all given functions + * + * This function is used for both request and response transforming + * + * @param {*} data Data to transform. + * @param {function(string=)} headers HTTP headers getter fn. + * @param {number} status HTTP status code of the response. + * @param {(Function|Array.)} fns Function or an array of functions. + * @returns {*} Transformed data. + */ +function transformData(data, headers, status, fns) { + if (isFunction(fns)) { + return fns(data, headers, status); + } + + forEach(fns, function(fn) { + data = fn(data, headers, status); + }); + + return data; +} + + +function isSuccess(status) { + return 200 <= status && status < 300; +} + + +/** + * @ngdoc provider + * @name $httpProvider + * @description + * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service. + * */ +function $HttpProvider() { + /** + * @ngdoc property + * @name $httpProvider#defaults + * @description + * + * Object containing default values for all {@link ng.$http $http} requests. + * + * - **`defaults.cache`** - {boolean|Object} - A boolean value or object created with + * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of HTTP responses + * by default. See {@link $http#caching $http Caching} for more information. + * + * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token. + * Defaults value is `'XSRF-TOKEN'`. + * + * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the + * XSRF token. Defaults value is `'X-XSRF-TOKEN'`. + * + * - **`defaults.headers`** - {Object} - Default headers for all $http requests. + * Refer to {@link ng.$http#setting-http-headers $http} for documentation on + * setting default headers. + * - **`defaults.headers.common`** + * - **`defaults.headers.post`** + * - **`defaults.headers.put`** + * - **`defaults.headers.patch`** + * + * + * - **`defaults.paramSerializer`** - `{string|function(Object):string}` - A function + * used to the prepare string representation of request parameters (specified as an object). + * If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}. + * Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}. + * + **/ + var defaults = this.defaults = { + // transform incoming response data + transformResponse: [defaultHttpResponseTransform], + + // transform outgoing request data + transformRequest: [function(d) { + return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d; + }], + + // default headers + headers: { + common: { + 'Accept': 'application/json, text/plain, */*' + }, + post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON), + put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON), + patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON) + }, + + xsrfCookieName: 'XSRF-TOKEN', + xsrfHeaderName: 'X-XSRF-TOKEN', + + paramSerializer: '$httpParamSerializer' + }; + + var useApplyAsync = false; + /** + * @ngdoc method + * @name $httpProvider#useApplyAsync + * @description + * + * Configure $http service to combine processing of multiple http responses received at around + * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in + * significant performance improvement for bigger applications that make many HTTP requests + * concurrently (common during application bootstrap). + * + * Defaults to false. If no value is specified, returns the current configured value. + * + * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred + * "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window + * to load and share the same digest cycle. + * + * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining. + * otherwise, returns the current configured value. + **/ + this.useApplyAsync = function(value) { + if (isDefined(value)) { + useApplyAsync = !!value; + return this; + } + return useApplyAsync; + }; + + var useLegacyPromise = true; + /** + * @ngdoc method + * @name $httpProvider#useLegacyPromiseExtensions + * @description + * + * Configure `$http` service to return promises without the shorthand methods `success` and `error`. + * This should be used to make sure that applications work without these methods. + * + * Defaults to true. If no value is specified, returns the current configured value. + * + * @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `success` and `error` methods. + * + * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining. + * otherwise, returns the current configured value. + **/ + this.useLegacyPromiseExtensions = function(value) { + if (isDefined(value)) { + useLegacyPromise = !!value; + return this; + } + return useLegacyPromise; + }; + + /** + * @ngdoc property + * @name $httpProvider#interceptors + * @description + * + * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http} + * pre-processing of request or postprocessing of responses. + * + * These service factories are ordered by request, i.e. they are applied in the same order as the + * array, on request, but reverse order, on response. + * + * {@link ng.$http#interceptors Interceptors detailed info} + **/ + var interceptorFactories = this.interceptors = []; + + this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector', + function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) { + + var defaultCache = $cacheFactory('$http'); + + /** + * Make sure that default param serializer is exposed as a function + */ + defaults.paramSerializer = isString(defaults.paramSerializer) ? + $injector.get(defaults.paramSerializer) : defaults.paramSerializer; + + /** + * Interceptors stored in reverse order. Inner interceptors before outer interceptors. + * The reversal is needed so that we can build up the interception chain around the + * server request. + */ + var reversedInterceptors = []; + + forEach(interceptorFactories, function(interceptorFactory) { + reversedInterceptors.unshift(isString(interceptorFactory) + ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory)); + }); + + /** + * @ngdoc service + * @kind function + * @name $http + * @requires ng.$httpBackend + * @requires $cacheFactory + * @requires $rootScope + * @requires $q + * @requires $injector + * + * @description + * The `$http` service is a core Angular service that facilitates communication with the remote + * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest) + * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP). + * + * For unit testing applications that use `$http` service, see + * {@link ngMock.$httpBackend $httpBackend mock}. + * + * For a higher level of abstraction, please check out the {@link ngResource.$resource + * $resource} service. + * + * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by + * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage + * it is important to familiarize yourself with these APIs and the guarantees they provide. + * + * + * ## General usage + * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} — + * that is used to generate an HTTP request and returns a {@link ng.$q promise}. + * + * ```js + * // Simple GET request example: + * $http({ + * method: 'GET', + * url: '/someUrl' + * }).then(function successCallback(response) { + * // this callback will be called asynchronously + * // when the response is available + * }, function errorCallback(response) { + * // called asynchronously if an error occurs + * // or server returns response with an error status. + * }); + * ``` + * + * The response object has these properties: + * + * - **data** – `{string|Object}` – The response body transformed with the transform + * functions. + * - **status** – `{number}` – HTTP status code of the response. + * - **headers** – `{function([headerName])}` – Header getter function. + * - **config** – `{Object}` – The configuration object that was used to generate the request. + * - **statusText** – `{string}` – HTTP status text of the response. + * + * A response status code between 200 and 299 is considered a success status and + * will result in the success callback being called. Note that if the response is a redirect, + * XMLHttpRequest will transparently follow it, meaning that the error callback will not be + * called for such responses. + * + * + * ## Shortcut methods + * + * Shortcut methods are also available. All shortcut methods require passing in the URL, and + * request data must be passed in for POST/PUT requests. An optional config can be passed as the + * last argument. + * + * ```js + * $http.get('/someUrl', config).then(successCallback, errorCallback); + * $http.post('/someUrl', data, config).then(successCallback, errorCallback); + * ``` + * + * Complete list of shortcut methods: + * + * - {@link ng.$http#get $http.get} + * - {@link ng.$http#head $http.head} + * - {@link ng.$http#post $http.post} + * - {@link ng.$http#put $http.put} + * - {@link ng.$http#delete $http.delete} + * - {@link ng.$http#jsonp $http.jsonp} + * - {@link ng.$http#patch $http.patch} + * + * + * ## Writing Unit Tests that use $http + * When unit testing (using {@link ngMock ngMock}), it is necessary to call + * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending + * request using trained responses. + * + * ``` + * $httpBackend.expectGET(...); + * $http.get(...); + * $httpBackend.flush(); + * ``` + * + * ## Deprecation Notice + *
    + * The `$http` legacy promise methods `success` and `error` have been deprecated. + * Use the standard `then` method instead. + * If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to + * `false` then these methods will throw {@link $http:legacy `$http/legacy`} error. + *
    + * + * ## Setting HTTP Headers + * + * The $http service will automatically add certain HTTP headers to all requests. These defaults + * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration + * object, which currently contains this default configuration: + * + * - `$httpProvider.defaults.headers.common` (headers that are common for all requests): + * - `Accept: application/json, text/plain, * / *` + * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests) + * - `Content-Type: application/json` + * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests) + * - `Content-Type: application/json` + * + * To add or overwrite these defaults, simply add or remove a property from these configuration + * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object + * with the lowercased HTTP method name as the key, e.g. + * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`. + * + * The defaults can also be set at runtime via the `$http.defaults` object in the same + * fashion. For example: + * + * ``` + * module.run(function($http) { + * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'; + * }); + * ``` + * + * In addition, you can supply a `headers` property in the config object passed when + * calling `$http(config)`, which overrides the defaults without changing them globally. + * + * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis, + * Use the `headers` property, setting the desired header to `undefined`. For example: + * + * ```js + * var req = { + * method: 'POST', + * url: 'http://example.com', + * headers: { + * 'Content-Type': undefined + * }, + * data: { test: 'test' } + * } + * + * $http(req).then(function(){...}, function(){...}); + * ``` + * + * ## Transforming Requests and Responses + * + * Both requests and responses can be transformed using transformation functions: `transformRequest` + * and `transformResponse`. These properties can be a single function that returns + * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions, + * which allows you to `push` or `unshift` a new transformation function into the transformation chain. + * + *
    + * **Note:** Angular does not make a copy of the `data` parameter before it is passed into the `transformRequest` pipeline. + * That means changes to the properties of `data` are not local to the transform function (since Javascript passes objects by reference). + * For example, when calling `$http.get(url, $scope.myObject)`, modifications to the object's properties in a transformRequest + * function will be reflected on the scope and in any templates where the object is data-bound. + * To prevent his, transform functions should have no side-effects. + * If you need to modify properties, it is recommended to make a copy of the data, or create new object to return. + *
    + * + * ### Default Transformations + * + * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and + * `defaults.transformResponse` properties. If a request does not provide its own transformations + * then these will be applied. + * + * You can augment or replace the default transformations by modifying these properties by adding to or + * replacing the array. + * + * Angular provides the following default transformations: + * + * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`): + * + * - If the `data` property of the request configuration object contains an object, serialize it + * into JSON format. + * + * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`): + * + * - If XSRF prefix is detected, strip it (see Security Considerations section below). + * - If JSON response is detected, deserialize it using a JSON parser. + * + * + * ### Overriding the Default Transformations Per Request + * + * If you wish override the request/response transformations only for a single request then provide + * `transformRequest` and/or `transformResponse` properties on the configuration object passed + * into `$http`. + * + * Note that if you provide these properties on the config object the default transformations will be + * overwritten. If you wish to augment the default transformations then you must include them in your + * local transformation array. + * + * The following code demonstrates adding a new response transformation to be run after the default response + * transformations have been run. + * + * ```js + * function appendTransform(defaults, transform) { + * + * // We can't guarantee that the default transformation is an array + * defaults = angular.isArray(defaults) ? defaults : [defaults]; + * + * // Append the new transformation to the defaults + * return defaults.concat(transform); + * } + * + * $http({ + * url: '...', + * method: 'GET', + * transformResponse: appendTransform($http.defaults.transformResponse, function(value) { + * return doTransform(value); + * }) + * }); + * ``` + * + * + * ## Caching + * + * {@link ng.$http `$http`} responses are not cached by default. To enable caching, you must + * set the config.cache value or the default cache value to TRUE or to a cache object (created + * with {@link ng.$cacheFactory `$cacheFactory`}). If defined, the value of config.cache takes + * precedence over the default cache value. + * + * In order to: + * * cache all responses - set the default cache value to TRUE or to a cache object + * * cache a specific response - set config.cache value to TRUE or to a cache object + * + * If caching is enabled, but neither the default cache nor config.cache are set to a cache object, + * then the default `$cacheFactory($http)` object is used. + * + * The default cache value can be set by updating the + * {@link ng.$http#defaults `$http.defaults.cache`} property or the + * {@link $httpProvider#defaults `$httpProvider.defaults.cache`} property. + * + * When caching is enabled, {@link ng.$http `$http`} stores the response from the server using + * the relevant cache object. The next time the same request is made, the response is returned + * from the cache without sending a request to the server. + * + * Take note that: + * + * * Only GET and JSONP requests are cached. + * * The cache key is the request URL including search parameters; headers are not considered. + * * Cached responses are returned asynchronously, in the same way as responses from the server. + * * If multiple identical requests are made using the same cache, which is not yet populated, + * one request will be made to the server and remaining requests will return the same response. + * * A cache-control header on the response does not affect if or how responses are cached. + * + * + * ## Interceptors + * + * Before you start creating interceptors, be sure to understand the + * {@link ng.$q $q and deferred/promise APIs}. + * + * For purposes of global error handling, authentication, or any kind of synchronous or + * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be + * able to intercept requests before they are handed to the server and + * responses before they are handed over to the application code that + * initiated these requests. The interceptors leverage the {@link ng.$q + * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing. + * + * The interceptors are service factories that are registered with the `$httpProvider` by + * adding them to the `$httpProvider.interceptors` array. The factory is called and + * injected with dependencies (if specified) and returns the interceptor. + * + * There are two kinds of interceptors (and two kinds of rejection interceptors): + * + * * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to + * modify the `config` object or create a new one. The function needs to return the `config` + * object directly, or a promise containing the `config` or a new `config` object. + * * `requestError`: interceptor gets called when a previous interceptor threw an error or + * resolved with a rejection. + * * `response`: interceptors get called with http `response` object. The function is free to + * modify the `response` object or create a new one. The function needs to return the `response` + * object directly, or as a promise containing the `response` or a new `response` object. + * * `responseError`: interceptor gets called when a previous interceptor threw an error or + * resolved with a rejection. + * + * + * ```js + * // register the interceptor as a service + * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) { + * return { + * // optional method + * 'request': function(config) { + * // do something on success + * return config; + * }, + * + * // optional method + * 'requestError': function(rejection) { + * // do something on error + * if (canRecover(rejection)) { + * return responseOrNewPromise + * } + * return $q.reject(rejection); + * }, + * + * + * + * // optional method + * 'response': function(response) { + * // do something on success + * return response; + * }, + * + * // optional method + * 'responseError': function(rejection) { + * // do something on error + * if (canRecover(rejection)) { + * return responseOrNewPromise + * } + * return $q.reject(rejection); + * } + * }; + * }); + * + * $httpProvider.interceptors.push('myHttpInterceptor'); + * + * + * // alternatively, register the interceptor via an anonymous factory + * $httpProvider.interceptors.push(function($q, dependency1, dependency2) { + * return { + * 'request': function(config) { + * // same as above + * }, + * + * 'response': function(response) { + * // same as above + * } + * }; + * }); + * ``` + * + * ## Security Considerations + * + * When designing web applications, consider security threats from: + * + * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx) + * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) + * + * Both server and the client must cooperate in order to eliminate these threats. Angular comes + * pre-configured with strategies that address these issues, but for this to work backend server + * cooperation is required. + * + * ### JSON Vulnerability Protection + * + * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx) + * allows third party website to turn your JSON resource URL into + * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To + * counter this your server can prefix all JSON requests with following string `")]}',\n"`. + * Angular will automatically strip the prefix before processing it as JSON. + * + * For example if your server needs to return: + * ```js + * ['one','two'] + * ``` + * + * which is vulnerable to attack, your server can return: + * ```js + * )]}', + * ['one','two'] + * ``` + * + * Angular will strip the prefix, before processing the JSON. + * + * + * ### Cross Site Request Forgery (XSRF) Protection + * + * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by + * which the attacker can trick an authenticated user into unknowingly executing actions on your + * website. Angular provides a mechanism to counter XSRF. When performing XHR requests, the + * $http service reads a token from a cookie (by default, `XSRF-TOKEN`) and sets it as an HTTP + * header (`X-XSRF-TOKEN`). Since only JavaScript that runs on your domain could read the + * cookie, your server can be assured that the XHR came from JavaScript running on your domain. + * The header will not be set for cross-domain requests. + * + * To take advantage of this, your server needs to set a token in a JavaScript readable session + * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the + * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure + * that only JavaScript running on your domain could have sent the request. The token must be + * unique for each user and must be verifiable by the server (to prevent the JavaScript from + * making up its own tokens). We recommend that the token is a digest of your site's + * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography)) + * for added security. + * + * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName + * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time, + * or the per-request config object. + * + * In order to prevent collisions in environments where multiple Angular apps share the + * same domain or subdomain, we recommend that each application uses unique cookie name. + * + * @param {object} config Object describing the request to be made and how it should be + * processed. The object has following properties: + * + * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc) + * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested. + * - **params** – `{Object.}` – Map of strings or objects which will be serialized + * with the `paramSerializer` and appended as GET parameters. + * - **data** – `{string|Object}` – Data to be sent as the request message data. + * - **headers** – `{Object}` – Map of strings or functions which return strings representing + * HTTP headers to send to the server. If the return value of a function is null, the + * header will not be sent. Functions accept a config object as an argument. + * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token. + * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token. + * - **transformRequest** – + * `{function(data, headersGetter)|Array.}` – + * transform function or an array of such functions. The transform function takes the http + * request body and headers and returns its transformed (typically serialized) version. + * See {@link ng.$http#overriding-the-default-transformations-per-request + * Overriding the Default Transformations} + * - **transformResponse** – + * `{function(data, headersGetter, status)|Array.}` – + * transform function or an array of such functions. The transform function takes the http + * response body, headers and status and returns its transformed (typically deserialized) version. + * See {@link ng.$http#overriding-the-default-transformations-per-request + * Overriding the Default Transformations} + * - **paramSerializer** - `{string|function(Object):string}` - A function used to + * prepare the string representation of request parameters (specified as an object). + * If specified as string, it is interpreted as function registered with the + * {@link $injector $injector}, which means you can create your own serializer + * by registering it as a {@link auto.$provide#service service}. + * The default serializer is the {@link $httpParamSerializer $httpParamSerializer}; + * alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike} + * - **cache** – `{boolean|Object}` – A boolean value or object created with + * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of the HTTP response. + * See {@link $http#caching $http Caching} for more information. + * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} + * that should abort the request when resolved. + * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the + * XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials) + * for more information. + * - **responseType** - `{string}` - see + * [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype). + * + * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object + * when the request succeeds or fails. + * + * + * @property {Array.} pendingRequests Array of config objects for currently pending + * requests. This is primarily meant to be used for debugging purposes. + * + * + * @example + + +
    + + +
    + + + +
    http status code: {{status}}
    +
    http response data: {{data}}
    +
    +
    + + angular.module('httpExample', []) + .controller('FetchController', ['$scope', '$http', '$templateCache', + function($scope, $http, $templateCache) { + $scope.method = 'GET'; + $scope.url = 'http-hello.html'; + + $scope.fetch = function() { + $scope.code = null; + $scope.response = null; + + $http({method: $scope.method, url: $scope.url, cache: $templateCache}). + then(function(response) { + $scope.status = response.status; + $scope.data = response.data; + }, function(response) { + $scope.data = response.data || "Request failed"; + $scope.status = response.status; + }); + }; + + $scope.updateModel = function(method, url) { + $scope.method = method; + $scope.url = url; + }; + }]); + + + Hello, $http! + + + var status = element(by.binding('status')); + var data = element(by.binding('data')); + var fetchBtn = element(by.id('fetchbtn')); + var sampleGetBtn = element(by.id('samplegetbtn')); + var sampleJsonpBtn = element(by.id('samplejsonpbtn')); + var invalidJsonpBtn = element(by.id('invalidjsonpbtn')); + + it('should make an xhr GET request', function() { + sampleGetBtn.click(); + fetchBtn.click(); + expect(status.getText()).toMatch('200'); + expect(data.getText()).toMatch(/Hello, \$http!/); + }); + +// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185 +// it('should make a JSONP request to angularjs.org', function() { +// sampleJsonpBtn.click(); +// fetchBtn.click(); +// expect(status.getText()).toMatch('200'); +// expect(data.getText()).toMatch(/Super Hero!/); +// }); + + it('should make JSONP request to invalid URL and invoke the error handler', + function() { + invalidJsonpBtn.click(); + fetchBtn.click(); + expect(status.getText()).toMatch('0'); + expect(data.getText()).toMatch('Request failed'); + }); + +
    + */ + function $http(requestConfig) { + + if (!isObject(requestConfig)) { + throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig); + } + + if (!isString(requestConfig.url)) { + throw minErr('$http')('badreq', 'Http request configuration url must be a string. Received: {0}', requestConfig.url); + } + + var config = extend({ + method: 'get', + transformRequest: defaults.transformRequest, + transformResponse: defaults.transformResponse, + paramSerializer: defaults.paramSerializer + }, requestConfig); + + config.headers = mergeHeaders(requestConfig); + config.method = uppercase(config.method); + config.paramSerializer = isString(config.paramSerializer) ? + $injector.get(config.paramSerializer) : config.paramSerializer; + + var serverRequest = function(config) { + var headers = config.headers; + var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest); + + // strip content-type if data is undefined + if (isUndefined(reqData)) { + forEach(headers, function(value, header) { + if (lowercase(header) === 'content-type') { + delete headers[header]; + } + }); + } + + if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) { + config.withCredentials = defaults.withCredentials; + } + + // send request + return sendReq(config, reqData).then(transformResponse, transformResponse); + }; + + var chain = [serverRequest, undefined]; + var promise = $q.when(config); + + // apply interceptors + forEach(reversedInterceptors, function(interceptor) { + if (interceptor.request || interceptor.requestError) { + chain.unshift(interceptor.request, interceptor.requestError); + } + if (interceptor.response || interceptor.responseError) { + chain.push(interceptor.response, interceptor.responseError); + } + }); + + while (chain.length) { + var thenFn = chain.shift(); + var rejectFn = chain.shift(); + + promise = promise.then(thenFn, rejectFn); + } + + if (useLegacyPromise) { + promise.success = function(fn) { + assertArgFn(fn, 'fn'); + + promise.then(function(response) { + fn(response.data, response.status, response.headers, config); + }); + return promise; + }; + + promise.error = function(fn) { + assertArgFn(fn, 'fn'); + + promise.then(null, function(response) { + fn(response.data, response.status, response.headers, config); + }); + return promise; + }; + } else { + promise.success = $httpMinErrLegacyFn('success'); + promise.error = $httpMinErrLegacyFn('error'); + } + + return promise; + + function transformResponse(response) { + // make a copy since the response must be cacheable + var resp = extend({}, response); + resp.data = transformData(response.data, response.headers, response.status, + config.transformResponse); + return (isSuccess(response.status)) + ? resp + : $q.reject(resp); + } + + function executeHeaderFns(headers, config) { + var headerContent, processedHeaders = {}; + + forEach(headers, function(headerFn, header) { + if (isFunction(headerFn)) { + headerContent = headerFn(config); + if (headerContent != null) { + processedHeaders[header] = headerContent; + } + } else { + processedHeaders[header] = headerFn; + } + }); + + return processedHeaders; + } + + function mergeHeaders(config) { + var defHeaders = defaults.headers, + reqHeaders = extend({}, config.headers), + defHeaderName, lowercaseDefHeaderName, reqHeaderName; + + defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]); + + // using for-in instead of forEach to avoid unnecessary iteration after header has been found + defaultHeadersIteration: + for (defHeaderName in defHeaders) { + lowercaseDefHeaderName = lowercase(defHeaderName); + + for (reqHeaderName in reqHeaders) { + if (lowercase(reqHeaderName) === lowercaseDefHeaderName) { + continue defaultHeadersIteration; + } + } + + reqHeaders[defHeaderName] = defHeaders[defHeaderName]; + } + + // execute if header value is a function for merged headers + return executeHeaderFns(reqHeaders, shallowCopy(config)); + } + } + + $http.pendingRequests = []; + + /** + * @ngdoc method + * @name $http#get + * + * @description + * Shortcut method to perform `GET` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name $http#delete + * + * @description + * Shortcut method to perform `DELETE` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name $http#head + * + * @description + * Shortcut method to perform `HEAD` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name $http#jsonp + * + * @description + * Shortcut method to perform `JSONP` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request. + * The name of the callback should be the string `JSON_CALLBACK`. + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + createShortMethods('get', 'delete', 'head', 'jsonp'); + + /** + * @ngdoc method + * @name $http#post + * + * @description + * Shortcut method to perform `POST` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {*} data Request content + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name $http#put + * + * @description + * Shortcut method to perform `PUT` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {*} data Request content + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name $http#patch + * + * @description + * Shortcut method to perform `PATCH` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {*} data Request content + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + createShortMethodsWithData('post', 'put', 'patch'); + + /** + * @ngdoc property + * @name $http#defaults + * + * @description + * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of + * default headers, withCredentials as well as request and response transformations. + * + * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above. + */ + $http.defaults = defaults; + + + return $http; + + + function createShortMethods(names) { + forEach(arguments, function(name) { + $http[name] = function(url, config) { + return $http(extend({}, config || {}, { + method: name, + url: url + })); + }; + }); + } + + + function createShortMethodsWithData(name) { + forEach(arguments, function(name) { + $http[name] = function(url, data, config) { + return $http(extend({}, config || {}, { + method: name, + url: url, + data: data + })); + }; + }); + } + + + /** + * Makes the request. + * + * !!! ACCESSES CLOSURE VARS: + * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests + */ + function sendReq(config, reqData) { + var deferred = $q.defer(), + promise = deferred.promise, + cache, + cachedResp, + reqHeaders = config.headers, + url = buildUrl(config.url, config.paramSerializer(config.params)); + + $http.pendingRequests.push(config); + promise.then(removePendingReq, removePendingReq); + + + if ((config.cache || defaults.cache) && config.cache !== false && + (config.method === 'GET' || config.method === 'JSONP')) { + cache = isObject(config.cache) ? config.cache + : isObject(defaults.cache) ? defaults.cache + : defaultCache; + } + + if (cache) { + cachedResp = cache.get(url); + if (isDefined(cachedResp)) { + if (isPromiseLike(cachedResp)) { + // cached request has already been sent, but there is no response yet + cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult); + } else { + // serving from cache + if (isArray(cachedResp)) { + resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]); + } else { + resolvePromise(cachedResp, 200, {}, 'OK'); + } + } + } else { + // put the promise for the non-transformed response into cache as a placeholder + cache.put(url, promise); + } + } + + + // if we won't have the response in cache, set the xsrf headers and + // send the request to the backend + if (isUndefined(cachedResp)) { + var xsrfValue = urlIsSameOrigin(config.url) + ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName] + : undefined; + if (xsrfValue) { + reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue; + } + + $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout, + config.withCredentials, config.responseType); + } + + return promise; + + + /** + * Callback registered to $httpBackend(): + * - caches the response if desired + * - resolves the raw $http promise + * - calls $apply + */ + function done(status, response, headersString, statusText) { + if (cache) { + if (isSuccess(status)) { + cache.put(url, [status, response, parseHeaders(headersString), statusText]); + } else { + // remove promise from the cache + cache.remove(url); + } + } + + function resolveHttpPromise() { + resolvePromise(response, status, headersString, statusText); + } + + if (useApplyAsync) { + $rootScope.$applyAsync(resolveHttpPromise); + } else { + resolveHttpPromise(); + if (!$rootScope.$$phase) $rootScope.$apply(); + } + } + + + /** + * Resolves the raw $http promise. + */ + function resolvePromise(response, status, headers, statusText) { + //status: HTTP response status code, 0, -1 (aborted by timeout / promise) + status = status >= -1 ? status : 0; + + (isSuccess(status) ? deferred.resolve : deferred.reject)({ + data: response, + status: status, + headers: headersGetter(headers), + config: config, + statusText: statusText + }); + } + + function resolvePromiseWithResult(result) { + resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText); + } + + function removePendingReq() { + var idx = $http.pendingRequests.indexOf(config); + if (idx !== -1) $http.pendingRequests.splice(idx, 1); + } + } + + + function buildUrl(url, serializedParams) { + if (serializedParams.length > 0) { + url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams; + } + return url; + } + }]; +} + +/** + * @ngdoc service + * @name $xhrFactory + * + * @description + * Factory function used to create XMLHttpRequest objects. + * + * Replace or decorate this service to create your own custom XMLHttpRequest objects. + * + * ``` + * angular.module('myApp', []) + * .factory('$xhrFactory', function() { + * return function createXhr(method, url) { + * return new window.XMLHttpRequest({mozSystem: true}); + * }; + * }); + * ``` + * + * @param {string} method HTTP method of the request (GET, POST, PUT, ..) + * @param {string} url URL of the request. + */ +function $xhrFactoryProvider() { + this.$get = function() { + return function createXhr() { + return new window.XMLHttpRequest(); + }; + }; +} + +/** + * @ngdoc service + * @name $httpBackend + * @requires $window + * @requires $document + * @requires $xhrFactory + * + * @description + * HTTP backend used by the {@link ng.$http service} that delegates to + * XMLHttpRequest object or JSONP and deals with browser incompatibilities. + * + * You should never need to use this service directly, instead use the higher-level abstractions: + * {@link ng.$http $http} or {@link ngResource.$resource $resource}. + * + * During testing this implementation is swapped with {@link ngMock.$httpBackend mock + * $httpBackend} which can be trained with responses. + */ +function $HttpBackendProvider() { + this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) { + return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]); + }]; +} + +function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) { + // TODO(vojta): fix the signature + return function(method, url, post, callback, headers, timeout, withCredentials, responseType) { + $browser.$$incOutstandingRequestCount(); + url = url || $browser.url(); + + if (lowercase(method) == 'jsonp') { + var callbackId = '_' + (callbacks.counter++).toString(36); + callbacks[callbackId] = function(data) { + callbacks[callbackId].data = data; + callbacks[callbackId].called = true; + }; + + var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId), + callbackId, function(status, text) { + completeRequest(callback, status, callbacks[callbackId].data, "", text); + callbacks[callbackId] = noop; + }); + } else { + + var xhr = createXhr(method, url); + + xhr.open(method, url, true); + forEach(headers, function(value, key) { + if (isDefined(value)) { + xhr.setRequestHeader(key, value); + } + }); + + xhr.onload = function requestLoaded() { + var statusText = xhr.statusText || ''; + + // responseText is the old-school way of retrieving response (supported by IE9) + // response/responseType properties were introduced in XHR Level2 spec (supported by IE10) + var response = ('response' in xhr) ? xhr.response : xhr.responseText; + + // normalize IE9 bug (http://bugs.jquery.com/ticket/1450) + var status = xhr.status === 1223 ? 204 : xhr.status; + + // fix status code when it is 0 (0 status is undocumented). + // Occurs when accessing file resources or on Android 4.1 stock browser + // while retrieving files from application cache. + if (status === 0) { + status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0; + } + + completeRequest(callback, + status, + response, + xhr.getAllResponseHeaders(), + statusText); + }; + + var requestError = function() { + // The response is always empty + // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error + completeRequest(callback, -1, null, null, ''); + }; + + xhr.onerror = requestError; + xhr.onabort = requestError; + + if (withCredentials) { + xhr.withCredentials = true; + } + + if (responseType) { + try { + xhr.responseType = responseType; + } catch (e) { + // WebKit added support for the json responseType value on 09/03/2013 + // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are + // known to throw when setting the value "json" as the response type. Other older + // browsers implementing the responseType + // + // The json response type can be ignored if not supported, because JSON payloads are + // parsed on the client-side regardless. + if (responseType !== 'json') { + throw e; + } + } + } + + xhr.send(isUndefined(post) ? null : post); + } + + if (timeout > 0) { + var timeoutId = $browserDefer(timeoutRequest, timeout); + } else if (isPromiseLike(timeout)) { + timeout.then(timeoutRequest); + } + + + function timeoutRequest() { + jsonpDone && jsonpDone(); + xhr && xhr.abort(); + } + + function completeRequest(callback, status, response, headersString, statusText) { + // cancel timeout and subsequent timeout promise resolution + if (isDefined(timeoutId)) { + $browserDefer.cancel(timeoutId); + } + jsonpDone = xhr = null; + + callback(status, response, headersString, statusText); + $browser.$$completeOutstandingRequest(noop); + } + }; + + function jsonpReq(url, callbackId, done) { + // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.: + // - fetches local scripts via XHR and evals them + // - adds and immediately removes script elements from the document + var script = rawDocument.createElement('script'), callback = null; + script.type = "text/javascript"; + script.src = url; + script.async = true; + + callback = function(event) { + removeEventListenerFn(script, "load", callback); + removeEventListenerFn(script, "error", callback); + rawDocument.body.removeChild(script); + script = null; + var status = -1; + var text = "unknown"; + + if (event) { + if (event.type === "load" && !callbacks[callbackId].called) { + event = { type: "error" }; + } + text = event.type; + status = event.type === "error" ? 404 : 200; + } + + if (done) { + done(status, text); + } + }; + + addEventListenerFn(script, "load", callback); + addEventListenerFn(script, "error", callback); + rawDocument.body.appendChild(script); + return callback; + } +} + +var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate'); +$interpolateMinErr.throwNoconcat = function(text) { + throw $interpolateMinErr('noconcat', + "Error while interpolating: {0}\nStrict Contextual Escaping disallows " + + "interpolations that concatenate multiple expressions when a trusted value is " + + "required. See http://docs.angularjs.org/api/ng.$sce", text); +}; + +$interpolateMinErr.interr = function(text, err) { + return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString()); +}; + +/** + * @ngdoc provider + * @name $interpolateProvider + * + * @description + * + * Used for configuring the interpolation markup. Defaults to `{{` and `}}`. + * + *
    + * This feature is sometimes used to mix different markup languages, e.g. to wrap an Angular + * template within a Python Jinja template (or any other template language). Mixing templating + * languages is **very dangerous**. The embedding template language will not safely escape Angular + * expressions, so any user-controlled values in the template will cause Cross Site Scripting (XSS) + * security bugs! + *
    + * + * @example + + + +
    + //demo.label// +
    +
    + + it('should interpolate binding with custom symbols', function() { + expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.'); + }); + +
    + */ +function $InterpolateProvider() { + var startSymbol = '{{'; + var endSymbol = '}}'; + + /** + * @ngdoc method + * @name $interpolateProvider#startSymbol + * @description + * Symbol to denote start of expression in the interpolated string. Defaults to `{{`. + * + * @param {string=} value new value to set the starting symbol to. + * @returns {string|self} Returns the symbol when used as getter and self if used as setter. + */ + this.startSymbol = function(value) { + if (value) { + startSymbol = value; + return this; + } else { + return startSymbol; + } + }; + + /** + * @ngdoc method + * @name $interpolateProvider#endSymbol + * @description + * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. + * + * @param {string=} value new value to set the ending symbol to. + * @returns {string|self} Returns the symbol when used as getter and self if used as setter. + */ + this.endSymbol = function(value) { + if (value) { + endSymbol = value; + return this; + } else { + return endSymbol; + } + }; + + + this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) { + var startSymbolLength = startSymbol.length, + endSymbolLength = endSymbol.length, + escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'), + escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g'); + + function escape(ch) { + return '\\\\\\' + ch; + } + + function unescapeText(text) { + return text.replace(escapedStartRegexp, startSymbol). + replace(escapedEndRegexp, endSymbol); + } + + function stringify(value) { + if (value == null) { // null || undefined + return ''; + } + switch (typeof value) { + case 'string': + break; + case 'number': + value = '' + value; + break; + default: + value = toJson(value); + } + + return value; + } + + //TODO: this is the same as the constantWatchDelegate in parse.js + function constantWatchDelegate(scope, listener, objectEquality, constantInterp) { + var unwatch; + return unwatch = scope.$watch(function constantInterpolateWatch(scope) { + unwatch(); + return constantInterp(scope); + }, listener, objectEquality); + } + + /** + * @ngdoc service + * @name $interpolate + * @kind function + * + * @requires $parse + * @requires $sce + * + * @description + * + * Compiles a string with markup into an interpolation function. This service is used by the + * HTML {@link ng.$compile $compile} service for data binding. See + * {@link ng.$interpolateProvider $interpolateProvider} for configuring the + * interpolation markup. + * + * + * ```js + * var $interpolate = ...; // injected + * var exp = $interpolate('Hello {{name | uppercase}}!'); + * expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!'); + * ``` + * + * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is + * `true`, the interpolation function will return `undefined` unless all embedded expressions + * evaluate to a value other than `undefined`. + * + * ```js + * var $interpolate = ...; // injected + * var context = {greeting: 'Hello', name: undefined }; + * + * // default "forgiving" mode + * var exp = $interpolate('{{greeting}} {{name}}!'); + * expect(exp(context)).toEqual('Hello !'); + * + * // "allOrNothing" mode + * exp = $interpolate('{{greeting}} {{name}}!', false, null, true); + * expect(exp(context)).toBeUndefined(); + * context.name = 'Angular'; + * expect(exp(context)).toEqual('Hello Angular!'); + * ``` + * + * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior. + * + * ####Escaped Interpolation + * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers + * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash). + * It will be rendered as a regular start/end marker, and will not be interpreted as an expression + * or binding. + * + * This enables web-servers to prevent script injection attacks and defacing attacks, to some + * degree, while also enabling code examples to work without relying on the + * {@link ng.directive:ngNonBindable ngNonBindable} directive. + * + * **For security purposes, it is strongly encouraged that web servers escape user-supplied data, + * replacing angle brackets (<, >) with &lt; and &gt; respectively, and replacing all + * interpolation start/end markers with their escaped counterparts.** + * + * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered + * output when the $interpolate service processes the text. So, for HTML elements interpolated + * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter + * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such, + * this is typically useful only when user-data is used in rendering a template from the server, or + * when otherwise untrusted data is used by a directive. + * + * + * + *
    + *

    {{apptitle}}: \{\{ username = "defaced value"; \}\} + *

    + *

    {{username}} attempts to inject code which will deface the + * application, but fails to accomplish their task, because the server has correctly + * escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash) + * characters.

    + *

    Instead, the result of the attempted script injection is visible, and can be removed + * from the database by an administrator.

    + *
    + *
    + *
    + * + * @param {string} text The text with markup to interpolate. + * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have + * embedded expression in order to return an interpolation function. Strings with no + * embedded expression will return null for the interpolation function. + * @param {string=} trustedContext when provided, the returned function passes the interpolated + * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult, + * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that + * provides Strict Contextual Escaping for details. + * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined + * unless all embedded expressions evaluate to a value other than `undefined`. + * @returns {function(context)} an interpolation function which is used to compute the + * interpolated string. The function has these parameters: + * + * - `context`: evaluation context for all expressions embedded in the interpolated text + */ + function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) { + // Provide a quick exit and simplified result function for text with no interpolation + if (!text.length || text.indexOf(startSymbol) === -1) { + var constantInterp; + if (!mustHaveExpression) { + var unescapedText = unescapeText(text); + constantInterp = valueFn(unescapedText); + constantInterp.exp = text; + constantInterp.expressions = []; + constantInterp.$$watchDelegate = constantWatchDelegate; + } + return constantInterp; + } + + allOrNothing = !!allOrNothing; + var startIndex, + endIndex, + index = 0, + expressions = [], + parseFns = [], + textLength = text.length, + exp, + concat = [], + expressionPositions = []; + + while (index < textLength) { + if (((startIndex = text.indexOf(startSymbol, index)) != -1) && + ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) { + if (index !== startIndex) { + concat.push(unescapeText(text.substring(index, startIndex))); + } + exp = text.substring(startIndex + startSymbolLength, endIndex); + expressions.push(exp); + parseFns.push($parse(exp, parseStringifyInterceptor)); + index = endIndex + endSymbolLength; + expressionPositions.push(concat.length); + concat.push(''); + } else { + // we did not find an interpolation, so we have to add the remainder to the separators array + if (index !== textLength) { + concat.push(unescapeText(text.substring(index))); + } + break; + } + } + + // Concatenating expressions makes it hard to reason about whether some combination of + // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a + // single expression be used for iframe[src], object[src], etc., we ensure that the value + // that's used is assigned or constructed by some JS code somewhere that is more testable or + // make it obvious that you bound the value to some user controlled value. This helps reduce + // the load when auditing for XSS issues. + if (trustedContext && concat.length > 1) { + $interpolateMinErr.throwNoconcat(text); + } + + if (!mustHaveExpression || expressions.length) { + var compute = function(values) { + for (var i = 0, ii = expressions.length; i < ii; i++) { + if (allOrNothing && isUndefined(values[i])) return; + concat[expressionPositions[i]] = values[i]; + } + return concat.join(''); + }; + + var getValue = function(value) { + return trustedContext ? + $sce.getTrusted(trustedContext, value) : + $sce.valueOf(value); + }; + + return extend(function interpolationFn(context) { + var i = 0; + var ii = expressions.length; + var values = new Array(ii); + + try { + for (; i < ii; i++) { + values[i] = parseFns[i](context); + } + + return compute(values); + } catch (err) { + $exceptionHandler($interpolateMinErr.interr(text, err)); + } + + }, { + // all of these properties are undocumented for now + exp: text, //just for compatibility with regular watchers created via $watch + expressions: expressions, + $$watchDelegate: function(scope, listener) { + var lastValue; + return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) { + var currValue = compute(values); + if (isFunction(listener)) { + listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope); + } + lastValue = currValue; + }); + } + }); + } + + function parseStringifyInterceptor(value) { + try { + value = getValue(value); + return allOrNothing && !isDefined(value) ? value : stringify(value); + } catch (err) { + $exceptionHandler($interpolateMinErr.interr(text, err)); + } + } + } + + + /** + * @ngdoc method + * @name $interpolate#startSymbol + * @description + * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`. + * + * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change + * the symbol. + * + * @returns {string} start symbol. + */ + $interpolate.startSymbol = function() { + return startSymbol; + }; + + + /** + * @ngdoc method + * @name $interpolate#endSymbol + * @description + * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. + * + * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change + * the symbol. + * + * @returns {string} end symbol. + */ + $interpolate.endSymbol = function() { + return endSymbol; + }; + + return $interpolate; + }]; +} + +function $IntervalProvider() { + this.$get = ['$rootScope', '$window', '$q', '$$q', '$browser', + function($rootScope, $window, $q, $$q, $browser) { + var intervals = {}; + + + /** + * @ngdoc service + * @name $interval + * + * @description + * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay` + * milliseconds. + * + * The return value of registering an interval function is a promise. This promise will be + * notified upon each tick of the interval, and will be resolved after `count` iterations, or + * run indefinitely if `count` is not defined. The value of the notification will be the + * number of iterations that have run. + * To cancel an interval, call `$interval.cancel(promise)`. + * + * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to + * move forward by `millis` milliseconds and trigger any functions scheduled to run in that + * time. + * + *
    + * **Note**: Intervals created by this service must be explicitly destroyed when you are finished + * with them. In particular they are not automatically destroyed when a controller's scope or a + * directive's element are destroyed. + * You should take this into consideration and make sure to always cancel the interval at the + * appropriate moment. See the example below for more details on how and when to do this. + *
    + * + * @param {function()} fn A function that should be called repeatedly. + * @param {number} delay Number of milliseconds between each function call. + * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat + * indefinitely. + * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise + * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. + * @param {...*=} Pass additional parameters to the executed function. + * @returns {promise} A promise which will be notified on each iteration. + * + * @example + * + * + * + * + *
    + *
    + *
    + * Current time is: + *
    + * Blood 1 : {{blood_1}} + * Blood 2 : {{blood_2}} + * + * + * + *
    + *
    + * + *
    + *
    + */ + function interval(fn, delay, count, invokeApply) { + var hasParams = arguments.length > 4, + args = hasParams ? sliceArgs(arguments, 4) : [], + setInterval = $window.setInterval, + clearInterval = $window.clearInterval, + iteration = 0, + skipApply = (isDefined(invokeApply) && !invokeApply), + deferred = (skipApply ? $$q : $q).defer(), + promise = deferred.promise; + + count = isDefined(count) ? count : 0; + + promise.$$intervalId = setInterval(function tick() { + if (skipApply) { + $browser.defer(callback); + } else { + $rootScope.$evalAsync(callback); + } + deferred.notify(iteration++); + + if (count > 0 && iteration >= count) { + deferred.resolve(iteration); + clearInterval(promise.$$intervalId); + delete intervals[promise.$$intervalId]; + } + + if (!skipApply) $rootScope.$apply(); + + }, delay); + + intervals[promise.$$intervalId] = deferred; + + return promise; + + function callback() { + if (!hasParams) { + fn(iteration); + } else { + fn.apply(null, args); + } + } + } + + + /** + * @ngdoc method + * @name $interval#cancel + * + * @description + * Cancels a task associated with the `promise`. + * + * @param {Promise=} promise returned by the `$interval` function. + * @returns {boolean} Returns `true` if the task was successfully canceled. + */ + interval.cancel = function(promise) { + if (promise && promise.$$intervalId in intervals) { + intervals[promise.$$intervalId].reject('canceled'); + $window.clearInterval(promise.$$intervalId); + delete intervals[promise.$$intervalId]; + return true; + } + return false; + }; + + return interval; + }]; +} + +/** + * @ngdoc service + * @name $locale + * + * @description + * $locale service provides localization rules for various Angular components. As of right now the + * only public api is: + * + * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`) + */ + +var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/, + DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21}; +var $locationMinErr = minErr('$location'); + + +/** + * Encode path using encodeUriSegment, ignoring forward slashes + * + * @param {string} path Path to encode + * @returns {string} + */ +function encodePath(path) { + var segments = path.split('/'), + i = segments.length; + + while (i--) { + segments[i] = encodeUriSegment(segments[i]); + } + + return segments.join('/'); +} + +function parseAbsoluteUrl(absoluteUrl, locationObj) { + var parsedUrl = urlResolve(absoluteUrl); + + locationObj.$$protocol = parsedUrl.protocol; + locationObj.$$host = parsedUrl.hostname; + locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null; +} + + +function parseAppUrl(relativeUrl, locationObj) { + var prefixed = (relativeUrl.charAt(0) !== '/'); + if (prefixed) { + relativeUrl = '/' + relativeUrl; + } + var match = urlResolve(relativeUrl); + locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ? + match.pathname.substring(1) : match.pathname); + locationObj.$$search = parseKeyValue(match.search); + locationObj.$$hash = decodeURIComponent(match.hash); + + // make sure path starts with '/'; + if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') { + locationObj.$$path = '/' + locationObj.$$path; + } +} + + +/** + * + * @param {string} begin + * @param {string} whole + * @returns {string} returns text from whole after begin or undefined if it does not begin with + * expected string. + */ +function beginsWith(begin, whole) { + if (whole.indexOf(begin) === 0) { + return whole.substr(begin.length); + } +} + + +function stripHash(url) { + var index = url.indexOf('#'); + return index == -1 ? url : url.substr(0, index); +} + +function trimEmptyHash(url) { + return url.replace(/(#.+)|#$/, '$1'); +} + + +function stripFile(url) { + return url.substr(0, stripHash(url).lastIndexOf('/') + 1); +} + +/* return the server only (scheme://host:port) */ +function serverBase(url) { + return url.substring(0, url.indexOf('/', url.indexOf('//') + 2)); +} + + +/** + * LocationHtml5Url represents an url + * This object is exposed as $location service when HTML5 mode is enabled and supported + * + * @constructor + * @param {string} appBase application base URL + * @param {string} appBaseNoFile application base URL stripped of any filename + * @param {string} basePrefix url path prefix + */ +function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) { + this.$$html5 = true; + basePrefix = basePrefix || ''; + parseAbsoluteUrl(appBase, this); + + + /** + * Parse given html5 (regular) url string into properties + * @param {string} url HTML5 url + * @private + */ + this.$$parse = function(url) { + var pathUrl = beginsWith(appBaseNoFile, url); + if (!isString(pathUrl)) { + throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url, + appBaseNoFile); + } + + parseAppUrl(pathUrl, this); + + if (!this.$$path) { + this.$$path = '/'; + } + + this.$$compose(); + }; + + /** + * Compose url and update `absUrl` property + * @private + */ + this.$$compose = function() { + var search = toKeyValue(this.$$search), + hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; + + this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; + this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/' + }; + + this.$$parseLinkUrl = function(url, relHref) { + if (relHref && relHref[0] === '#') { + // special case for links to hash fragments: + // keep the old url and only replace the hash fragment + this.hash(relHref.slice(1)); + return true; + } + var appUrl, prevAppUrl; + var rewrittenUrl; + + if (isDefined(appUrl = beginsWith(appBase, url))) { + prevAppUrl = appUrl; + if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) { + rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl); + } else { + rewrittenUrl = appBase + prevAppUrl; + } + } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) { + rewrittenUrl = appBaseNoFile + appUrl; + } else if (appBaseNoFile == url + '/') { + rewrittenUrl = appBaseNoFile; + } + if (rewrittenUrl) { + this.$$parse(rewrittenUrl); + } + return !!rewrittenUrl; + }; +} + + +/** + * LocationHashbangUrl represents url + * This object is exposed as $location service when developer doesn't opt into html5 mode. + * It also serves as the base class for html5 mode fallback on legacy browsers. + * + * @constructor + * @param {string} appBase application base URL + * @param {string} appBaseNoFile application base URL stripped of any filename + * @param {string} hashPrefix hashbang prefix + */ +function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) { + + parseAbsoluteUrl(appBase, this); + + + /** + * Parse given hashbang url into properties + * @param {string} url Hashbang url + * @private + */ + this.$$parse = function(url) { + var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url); + var withoutHashUrl; + + if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') { + + // The rest of the url starts with a hash so we have + // got either a hashbang path or a plain hash fragment + withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl); + if (isUndefined(withoutHashUrl)) { + // There was no hashbang prefix so we just have a hash fragment + withoutHashUrl = withoutBaseUrl; + } + + } else { + // There was no hashbang path nor hash fragment: + // If we are in HTML5 mode we use what is left as the path; + // Otherwise we ignore what is left + if (this.$$html5) { + withoutHashUrl = withoutBaseUrl; + } else { + withoutHashUrl = ''; + if (isUndefined(withoutBaseUrl)) { + appBase = url; + this.replace(); + } + } + } + + parseAppUrl(withoutHashUrl, this); + + this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase); + + this.$$compose(); + + /* + * In Windows, on an anchor node on documents loaded from + * the filesystem, the browser will return a pathname + * prefixed with the drive name ('/C:/path') when a + * pathname without a drive is set: + * * a.setAttribute('href', '/foo') + * * a.pathname === '/C:/foo' //true + * + * Inside of Angular, we're always using pathnames that + * do not include drive names for routing. + */ + function removeWindowsDriveName(path, url, base) { + /* + Matches paths for file protocol on windows, + such as /C:/foo/bar, and captures only /foo/bar. + */ + var windowsFilePathExp = /^\/[A-Z]:(\/.*)/; + + var firstPathSegmentMatch; + + //Get the relative path from the input URL. + if (url.indexOf(base) === 0) { + url = url.replace(base, ''); + } + + // The input URL intentionally contains a first path segment that ends with a colon. + if (windowsFilePathExp.exec(url)) { + return path; + } + + firstPathSegmentMatch = windowsFilePathExp.exec(path); + return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path; + } + }; + + /** + * Compose hashbang url and update `absUrl` property + * @private + */ + this.$$compose = function() { + var search = toKeyValue(this.$$search), + hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; + + this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; + this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : ''); + }; + + this.$$parseLinkUrl = function(url, relHref) { + if (stripHash(appBase) == stripHash(url)) { + this.$$parse(url); + return true; + } + return false; + }; +} + + +/** + * LocationHashbangUrl represents url + * This object is exposed as $location service when html5 history api is enabled but the browser + * does not support it. + * + * @constructor + * @param {string} appBase application base URL + * @param {string} appBaseNoFile application base URL stripped of any filename + * @param {string} hashPrefix hashbang prefix + */ +function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) { + this.$$html5 = true; + LocationHashbangUrl.apply(this, arguments); + + this.$$parseLinkUrl = function(url, relHref) { + if (relHref && relHref[0] === '#') { + // special case for links to hash fragments: + // keep the old url and only replace the hash fragment + this.hash(relHref.slice(1)); + return true; + } + + var rewrittenUrl; + var appUrl; + + if (appBase == stripHash(url)) { + rewrittenUrl = url; + } else if ((appUrl = beginsWith(appBaseNoFile, url))) { + rewrittenUrl = appBase + hashPrefix + appUrl; + } else if (appBaseNoFile === url + '/') { + rewrittenUrl = appBaseNoFile; + } + if (rewrittenUrl) { + this.$$parse(rewrittenUrl); + } + return !!rewrittenUrl; + }; + + this.$$compose = function() { + var search = toKeyValue(this.$$search), + hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; + + this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; + // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#' + this.$$absUrl = appBase + hashPrefix + this.$$url; + }; + +} + + +var locationPrototype = { + + /** + * Are we in html5 mode? + * @private + */ + $$html5: false, + + /** + * Has any change been replacing? + * @private + */ + $$replace: false, + + /** + * @ngdoc method + * @name $location#absUrl + * + * @description + * This method is getter only. + * + * Return full url representation with all segments encoded according to rules specified in + * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt). + * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo + * var absUrl = $location.absUrl(); + * // => "http://example.com/#/some/path?foo=bar&baz=xoxo" + * ``` + * + * @return {string} full url + */ + absUrl: locationGetter('$$absUrl'), + + /** + * @ngdoc method + * @name $location#url + * + * @description + * This method is getter / setter. + * + * Return url (e.g. `/path?a=b#hash`) when called without any parameter. + * + * Change path, search and hash, when called with parameter and return `$location`. + * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo + * var url = $location.url(); + * // => "/some/path?foo=bar&baz=xoxo" + * ``` + * + * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`) + * @return {string} url + */ + url: function(url) { + if (isUndefined(url)) { + return this.$$url; + } + + var match = PATH_MATCH.exec(url); + if (match[1] || url === '') this.path(decodeURIComponent(match[1])); + if (match[2] || match[1] || url === '') this.search(match[3] || ''); + this.hash(match[5] || ''); + + return this; + }, + + /** + * @ngdoc method + * @name $location#protocol + * + * @description + * This method is getter only. + * + * Return protocol of current url. + * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo + * var protocol = $location.protocol(); + * // => "http" + * ``` + * + * @return {string} protocol of current url + */ + protocol: locationGetter('$$protocol'), + + /** + * @ngdoc method + * @name $location#host + * + * @description + * This method is getter only. + * + * Return host of current url. + * + * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only. + * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo + * var host = $location.host(); + * // => "example.com" + * + * // given url http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo + * host = $location.host(); + * // => "example.com" + * host = location.host; + * // => "example.com:8080" + * ``` + * + * @return {string} host of current url. + */ + host: locationGetter('$$host'), + + /** + * @ngdoc method + * @name $location#port + * + * @description + * This method is getter only. + * + * Return port of current url. + * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo + * var port = $location.port(); + * // => 80 + * ``` + * + * @return {Number} port + */ + port: locationGetter('$$port'), + + /** + * @ngdoc method + * @name $location#path + * + * @description + * This method is getter / setter. + * + * Return path of current url when called without any parameter. + * + * Change path when called with parameter and return `$location`. + * + * Note: Path should always begin with forward slash (/), this method will add the forward slash + * if it is missing. + * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo + * var path = $location.path(); + * // => "/some/path" + * ``` + * + * @param {(string|number)=} path New path + * @return {string} path + */ + path: locationGetterSetter('$$path', function(path) { + path = path !== null ? path.toString() : ''; + return path.charAt(0) == '/' ? path : '/' + path; + }), + + /** + * @ngdoc method + * @name $location#search + * + * @description + * This method is getter / setter. + * + * Return search part (as object) of current url when called without any parameter. + * + * Change search part when called with parameter and return `$location`. + * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo + * var searchObject = $location.search(); + * // => {foo: 'bar', baz: 'xoxo'} + * + * // set foo to 'yipee' + * $location.search('foo', 'yipee'); + * // $location.search() => {foo: 'yipee', baz: 'xoxo'} + * ``` + * + * @param {string|Object.|Object.>} search New search params - string or + * hash object. + * + * When called with a single argument the method acts as a setter, setting the `search` component + * of `$location` to the specified value. + * + * If the argument is a hash object containing an array of values, these values will be encoded + * as duplicate search parameters in the url. + * + * @param {(string|Number|Array|boolean)=} paramValue If `search` is a string or number, then `paramValue` + * will override only a single search property. + * + * If `paramValue` is an array, it will override the property of the `search` component of + * `$location` specified via the first argument. + * + * If `paramValue` is `null`, the property specified via the first argument will be deleted. + * + * If `paramValue` is `true`, the property specified via the first argument will be added with no + * value nor trailing equal sign. + * + * @return {Object} If called with no arguments returns the parsed `search` object. If called with + * one or more arguments returns `$location` object itself. + */ + search: function(search, paramValue) { + switch (arguments.length) { + case 0: + return this.$$search; + case 1: + if (isString(search) || isNumber(search)) { + search = search.toString(); + this.$$search = parseKeyValue(search); + } else if (isObject(search)) { + search = copy(search, {}); + // remove object undefined or null properties + forEach(search, function(value, key) { + if (value == null) delete search[key]; + }); + + this.$$search = search; + } else { + throw $locationMinErr('isrcharg', + 'The first argument of the `$location#search()` call must be a string or an object.'); + } + break; + default: + if (isUndefined(paramValue) || paramValue === null) { + delete this.$$search[search]; + } else { + this.$$search[search] = paramValue; + } + } + + this.$$compose(); + return this; + }, + + /** + * @ngdoc method + * @name $location#hash + * + * @description + * This method is getter / setter. + * + * Returns the hash fragment when called without any parameters. + * + * Changes the hash fragment when called with a parameter and returns `$location`. + * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue + * var hash = $location.hash(); + * // => "hashValue" + * ``` + * + * @param {(string|number)=} hash New hash fragment + * @return {string} hash + */ + hash: locationGetterSetter('$$hash', function(hash) { + return hash !== null ? hash.toString() : ''; + }), + + /** + * @ngdoc method + * @name $location#replace + * + * @description + * If called, all changes to $location during the current `$digest` will replace the current history + * record, instead of adding a new one. + */ + replace: function() { + this.$$replace = true; + return this; + } +}; + +forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) { + Location.prototype = Object.create(locationPrototype); + + /** + * @ngdoc method + * @name $location#state + * + * @description + * This method is getter / setter. + * + * Return the history state object when called without any parameter. + * + * Change the history state object when called with one parameter and return `$location`. + * The state object is later passed to `pushState` or `replaceState`. + * + * NOTE: This method is supported only in HTML5 mode and only in browsers supporting + * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support + * older browsers (like IE9 or Android < 4.0), don't use this method. + * + * @param {object=} state State object for pushState or replaceState + * @return {object} state + */ + Location.prototype.state = function(state) { + if (!arguments.length) { + return this.$$state; + } + + if (Location !== LocationHtml5Url || !this.$$html5) { + throw $locationMinErr('nostate', 'History API state support is available only ' + + 'in HTML5 mode and only in browsers supporting HTML5 History API'); + } + // The user might modify `stateObject` after invoking `$location.state(stateObject)` + // but we're changing the $$state reference to $browser.state() during the $digest + // so the modification window is narrow. + this.$$state = isUndefined(state) ? null : state; + + return this; + }; +}); + + +function locationGetter(property) { + return function() { + return this[property]; + }; +} + + +function locationGetterSetter(property, preprocess) { + return function(value) { + if (isUndefined(value)) { + return this[property]; + } + + this[property] = preprocess(value); + this.$$compose(); + + return this; + }; +} + + +/** + * @ngdoc service + * @name $location + * + * @requires $rootElement + * + * @description + * The $location service parses the URL in the browser address bar (based on the + * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL + * available to your application. Changes to the URL in the address bar are reflected into + * $location service and changes to $location are reflected into the browser address bar. + * + * **The $location service:** + * + * - Exposes the current URL in the browser address bar, so you can + * - Watch and observe the URL. + * - Change the URL. + * - Synchronizes the URL with the browser when the user + * - Changes the address bar. + * - Clicks the back or forward button (or clicks a History link). + * - Clicks on a link. + * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash). + * + * For more information see {@link guide/$location Developer Guide: Using $location} + */ + +/** + * @ngdoc provider + * @name $locationProvider + * @description + * Use the `$locationProvider` to configure how the application deep linking paths are stored. + */ +function $LocationProvider() { + var hashPrefix = '', + html5Mode = { + enabled: false, + requireBase: true, + rewriteLinks: true + }; + + /** + * @ngdoc method + * @name $locationProvider#hashPrefix + * @description + * @param {string=} prefix Prefix for hash part (containing path and search) + * @returns {*} current value if used as getter or itself (chaining) if used as setter + */ + this.hashPrefix = function(prefix) { + if (isDefined(prefix)) { + hashPrefix = prefix; + return this; + } else { + return hashPrefix; + } + }; + + /** + * @ngdoc method + * @name $locationProvider#html5Mode + * @description + * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value. + * If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported + * properties: + * - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to + * change urls where supported. Will fall back to hash-prefixed paths in browsers that do not + * support `pushState`. + * - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies + * whether or not a tag is required to be present. If `enabled` and `requireBase` are + * true, and a base tag is not present, an error will be thrown when `$location` is injected. + * See the {@link guide/$location $location guide for more information} + * - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled, + * enables/disables url rewriting for relative links. + * + * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter + */ + this.html5Mode = function(mode) { + if (isBoolean(mode)) { + html5Mode.enabled = mode; + return this; + } else if (isObject(mode)) { + + if (isBoolean(mode.enabled)) { + html5Mode.enabled = mode.enabled; + } + + if (isBoolean(mode.requireBase)) { + html5Mode.requireBase = mode.requireBase; + } + + if (isBoolean(mode.rewriteLinks)) { + html5Mode.rewriteLinks = mode.rewriteLinks; + } + + return this; + } else { + return html5Mode; + } + }; + + /** + * @ngdoc event + * @name $location#$locationChangeStart + * @eventType broadcast on root scope + * @description + * Broadcasted before a URL will change. + * + * This change can be prevented by calling + * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more + * details about event object. Upon successful change + * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired. + * + * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when + * the browser supports the HTML5 History API. + * + * @param {Object} angularEvent Synthetic event object. + * @param {string} newUrl New URL + * @param {string=} oldUrl URL that was before it was changed. + * @param {string=} newState New history state object + * @param {string=} oldState History state object that was before it was changed. + */ + + /** + * @ngdoc event + * @name $location#$locationChangeSuccess + * @eventType broadcast on root scope + * @description + * Broadcasted after a URL was changed. + * + * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when + * the browser supports the HTML5 History API. + * + * @param {Object} angularEvent Synthetic event object. + * @param {string} newUrl New URL + * @param {string=} oldUrl URL that was before it was changed. + * @param {string=} newState New history state object + * @param {string=} oldState History state object that was before it was changed. + */ + + this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window', + function($rootScope, $browser, $sniffer, $rootElement, $window) { + var $location, + LocationMode, + baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to '' + initialUrl = $browser.url(), + appBase; + + if (html5Mode.enabled) { + if (!baseHref && html5Mode.requireBase) { + throw $locationMinErr('nobase', + "$location in HTML5 mode requires a tag to be present!"); + } + appBase = serverBase(initialUrl) + (baseHref || '/'); + LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url; + } else { + appBase = stripHash(initialUrl); + LocationMode = LocationHashbangUrl; + } + var appBaseNoFile = stripFile(appBase); + + $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix); + $location.$$parseLinkUrl(initialUrl, initialUrl); + + $location.$$state = $browser.state(); + + var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i; + + function setBrowserUrlWithFallback(url, replace, state) { + var oldUrl = $location.url(); + var oldState = $location.$$state; + try { + $browser.url(url, replace, state); + + // Make sure $location.state() returns referentially identical (not just deeply equal) + // state object; this makes possible quick checking if the state changed in the digest + // loop. Checking deep equality would be too expensive. + $location.$$state = $browser.state(); + } catch (e) { + // Restore old values if pushState fails + $location.url(oldUrl); + $location.$$state = oldState; + + throw e; + } + } + + $rootElement.on('click', function(event) { + // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser) + // currently we open nice url link and redirect then + + if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return; + + var elm = jqLite(event.target); + + // traverse the DOM up to find first A tag + while (nodeName_(elm[0]) !== 'a') { + // ignore rewriting if no A tag (reached root element, or no parent - removed from document) + if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return; + } + + var absHref = elm.prop('href'); + // get the actual href attribute - see + // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx + var relHref = elm.attr('href') || elm.attr('xlink:href'); + + if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') { + // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during + // an animation. + absHref = urlResolve(absHref.animVal).href; + } + + // Ignore when url is started with javascript: or mailto: + if (IGNORE_URI_REGEXP.test(absHref)) return; + + if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) { + if ($location.$$parseLinkUrl(absHref, relHref)) { + // We do a preventDefault for all urls that are part of the angular application, + // in html5mode and also without, so that we are able to abort navigation without + // getting double entries in the location history. + event.preventDefault(); + // update location manually + if ($location.absUrl() != $browser.url()) { + $rootScope.$apply(); + // hack to work around FF6 bug 684208 when scenario runner clicks on links + $window.angular['ff-684208-preventDefault'] = true; + } + } + } + }); + + + // rewrite hashbang url <> html5 url + if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) { + $browser.url($location.absUrl(), true); + } + + var initializing = true; + + // update $location when $browser url changes + $browser.onUrlChange(function(newUrl, newState) { + + if (isUndefined(beginsWith(appBaseNoFile, newUrl))) { + // If we are navigating outside of the app then force a reload + $window.location.href = newUrl; + return; + } + + $rootScope.$evalAsync(function() { + var oldUrl = $location.absUrl(); + var oldState = $location.$$state; + var defaultPrevented; + newUrl = trimEmptyHash(newUrl); + $location.$$parse(newUrl); + $location.$$state = newState; + + defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, + newState, oldState).defaultPrevented; + + // if the location was changed by a `$locationChangeStart` handler then stop + // processing this location change + if ($location.absUrl() !== newUrl) return; + + if (defaultPrevented) { + $location.$$parse(oldUrl); + $location.$$state = oldState; + setBrowserUrlWithFallback(oldUrl, false, oldState); + } else { + initializing = false; + afterLocationChange(oldUrl, oldState); + } + }); + if (!$rootScope.$$phase) $rootScope.$digest(); + }); + + // update browser + $rootScope.$watch(function $locationWatch() { + var oldUrl = trimEmptyHash($browser.url()); + var newUrl = trimEmptyHash($location.absUrl()); + var oldState = $browser.state(); + var currentReplace = $location.$$replace; + var urlOrStateChanged = oldUrl !== newUrl || + ($location.$$html5 && $sniffer.history && oldState !== $location.$$state); + + if (initializing || urlOrStateChanged) { + initializing = false; + + $rootScope.$evalAsync(function() { + var newUrl = $location.absUrl(); + var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, + $location.$$state, oldState).defaultPrevented; + + // if the location was changed by a `$locationChangeStart` handler then stop + // processing this location change + if ($location.absUrl() !== newUrl) return; + + if (defaultPrevented) { + $location.$$parse(oldUrl); + $location.$$state = oldState; + } else { + if (urlOrStateChanged) { + setBrowserUrlWithFallback(newUrl, currentReplace, + oldState === $location.$$state ? null : $location.$$state); + } + afterLocationChange(oldUrl, oldState); + } + }); + } + + $location.$$replace = false; + + // we don't need to return anything because $evalAsync will make the digest loop dirty when + // there is a change + }); + + return $location; + + function afterLocationChange(oldUrl, oldState) { + $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl, + $location.$$state, oldState); + } +}]; +} + +/** + * @ngdoc service + * @name $log + * @requires $window + * + * @description + * Simple service for logging. Default implementation safely writes the message + * into the browser's console (if present). + * + * The main purpose of this service is to simplify debugging and troubleshooting. + * + * The default is to log `debug` messages. You can use + * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this. + * + * @example + + + angular.module('logExample', []) + .controller('LogController', ['$scope', '$log', function($scope, $log) { + $scope.$log = $log; + $scope.message = 'Hello World!'; + }]); + + +
    +

    Reload this page with open console, enter text and hit the log button...

    + + + + + + +
    +
    +
    + */ + +/** + * @ngdoc provider + * @name $logProvider + * @description + * Use the `$logProvider` to configure how the application logs messages + */ +function $LogProvider() { + var debug = true, + self = this; + + /** + * @ngdoc method + * @name $logProvider#debugEnabled + * @description + * @param {boolean=} flag enable or disable debug level messages + * @returns {*} current value if used as getter or itself (chaining) if used as setter + */ + this.debugEnabled = function(flag) { + if (isDefined(flag)) { + debug = flag; + return this; + } else { + return debug; + } + }; + + this.$get = ['$window', function($window) { + return { + /** + * @ngdoc method + * @name $log#log + * + * @description + * Write a log message + */ + log: consoleLog('log'), + + /** + * @ngdoc method + * @name $log#info + * + * @description + * Write an information message + */ + info: consoleLog('info'), + + /** + * @ngdoc method + * @name $log#warn + * + * @description + * Write a warning message + */ + warn: consoleLog('warn'), + + /** + * @ngdoc method + * @name $log#error + * + * @description + * Write an error message + */ + error: consoleLog('error'), + + /** + * @ngdoc method + * @name $log#debug + * + * @description + * Write a debug message + */ + debug: (function() { + var fn = consoleLog('debug'); + + return function() { + if (debug) { + fn.apply(self, arguments); + } + }; + }()) + }; + + function formatError(arg) { + if (arg instanceof Error) { + if (arg.stack) { + arg = (arg.message && arg.stack.indexOf(arg.message) === -1) + ? 'Error: ' + arg.message + '\n' + arg.stack + : arg.stack; + } else if (arg.sourceURL) { + arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line; + } + } + return arg; + } + + function consoleLog(type) { + var console = $window.console || {}, + logFn = console[type] || console.log || noop, + hasApply = false; + + // Note: reading logFn.apply throws an error in IE11 in IE8 document mode. + // The reason behind this is that console.log has type "object" in IE8... + try { + hasApply = !!logFn.apply; + } catch (e) {} + + if (hasApply) { + return function() { + var args = []; + forEach(arguments, function(arg) { + args.push(formatError(arg)); + }); + return logFn.apply(console, args); + }; + } + + // we are IE which either doesn't have window.console => this is noop and we do nothing, + // or we are IE where console.log doesn't have apply so we log at least first 2 args + return function(arg1, arg2) { + logFn(arg1, arg2 == null ? '' : arg2); + }; + } + }]; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Any commits to this file should be reviewed with security in mind. * + * Changes to this file can potentially create security vulnerabilities. * + * An approval from 2 Core members with history of modifying * + * this file is required. * + * * + * Does the change somehow allow for arbitrary javascript to be executed? * + * Or allows for someone to change the prototype of built-in objects? * + * Or gives undesired access to variables likes document or window? * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +var $parseMinErr = minErr('$parse'); + +// Sandboxing Angular Expressions +// ------------------------------ +// Angular expressions are generally considered safe because these expressions only have direct +// access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by +// obtaining a reference to native JS functions such as the Function constructor. +// +// As an example, consider the following Angular expression: +// +// {}.toString.constructor('alert("evil JS code")') +// +// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits +// against the expression language, but not to prevent exploits that were enabled by exposing +// sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good +// practice and therefore we are not even trying to protect against interaction with an object +// explicitly exposed in this way. +// +// In general, it is not possible to access a Window object from an angular expression unless a +// window or some DOM object that has a reference to window is published onto a Scope. +// Similarly we prevent invocations of function known to be dangerous, as well as assignments to +// native objects. +// +// See https://docs.angularjs.org/guide/security + + +function ensureSafeMemberName(name, fullExpression) { + if (name === "__defineGetter__" || name === "__defineSetter__" + || name === "__lookupGetter__" || name === "__lookupSetter__" + || name === "__proto__") { + throw $parseMinErr('isecfld', + 'Attempting to access a disallowed field in Angular expressions! ' + + 'Expression: {0}', fullExpression); + } + return name; +} + +function getStringValue(name) { + // Property names must be strings. This means that non-string objects cannot be used + // as keys in an object. Any non-string object, including a number, is typecasted + // into a string via the toString method. + // -- MDN, https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors#Property_names + // + // So, to ensure that we are checking the same `name` that JavaScript would use, we cast it + // to a string. It's not always possible. If `name` is an object and its `toString` method is + // 'broken' (doesn't return a string, isn't a function, etc.), an error will be thrown: + // + // TypeError: Cannot convert object to primitive value + // + // For performance reasons, we don't catch this error here and allow it to propagate up the call + // stack. Note that you'll get the same error in JavaScript if you try to access a property using + // such a 'broken' object as a key. + return name + ''; +} + +function ensureSafeObject(obj, fullExpression) { + // nifty check if obj is Function that is fast and works across iframes and other contexts + if (obj) { + if (obj.constructor === obj) { + throw $parseMinErr('isecfn', + 'Referencing Function in Angular expressions is disallowed! Expression: {0}', + fullExpression); + } else if (// isWindow(obj) + obj.window === obj) { + throw $parseMinErr('isecwindow', + 'Referencing the Window in Angular expressions is disallowed! Expression: {0}', + fullExpression); + } else if (// isElement(obj) + obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) { + throw $parseMinErr('isecdom', + 'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}', + fullExpression); + } else if (// block Object so that we can't get hold of dangerous Object.* methods + obj === Object) { + throw $parseMinErr('isecobj', + 'Referencing Object in Angular expressions is disallowed! Expression: {0}', + fullExpression); + } + } + return obj; +} + +var CALL = Function.prototype.call; +var APPLY = Function.prototype.apply; +var BIND = Function.prototype.bind; + +function ensureSafeFunction(obj, fullExpression) { + if (obj) { + if (obj.constructor === obj) { + throw $parseMinErr('isecfn', + 'Referencing Function in Angular expressions is disallowed! Expression: {0}', + fullExpression); + } else if (obj === CALL || obj === APPLY || obj === BIND) { + throw $parseMinErr('isecff', + 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}', + fullExpression); + } + } +} + +function ensureSafeAssignContext(obj, fullExpression) { + if (obj) { + if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor || + obj === {}.constructor || obj === [].constructor || obj === Function.constructor) { + throw $parseMinErr('isecaf', + 'Assigning to a constructor is disallowed! Expression: {0}', fullExpression); + } + } +} + +var OPERATORS = createMap(); +forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; }); +var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; + + +///////////////////////////////////////// + + +/** + * @constructor + */ +var Lexer = function(options) { + this.options = options; +}; + +Lexer.prototype = { + constructor: Lexer, + + lex: function(text) { + this.text = text; + this.index = 0; + this.tokens = []; + + while (this.index < this.text.length) { + var ch = this.text.charAt(this.index); + if (ch === '"' || ch === "'") { + this.readString(ch); + } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) { + this.readNumber(); + } else if (this.isIdent(ch)) { + this.readIdent(); + } else if (this.is(ch, '(){}[].,;:?')) { + this.tokens.push({index: this.index, text: ch}); + this.index++; + } else if (this.isWhitespace(ch)) { + this.index++; + } else { + var ch2 = ch + this.peek(); + var ch3 = ch2 + this.peek(2); + var op1 = OPERATORS[ch]; + var op2 = OPERATORS[ch2]; + var op3 = OPERATORS[ch3]; + if (op1 || op2 || op3) { + var token = op3 ? ch3 : (op2 ? ch2 : ch); + this.tokens.push({index: this.index, text: token, operator: true}); + this.index += token.length; + } else { + this.throwError('Unexpected next character ', this.index, this.index + 1); + } + } + } + return this.tokens; + }, + + is: function(ch, chars) { + return chars.indexOf(ch) !== -1; + }, + + peek: function(i) { + var num = i || 1; + return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false; + }, + + isNumber: function(ch) { + return ('0' <= ch && ch <= '9') && typeof ch === "string"; + }, + + isWhitespace: function(ch) { + // IE treats non-breaking space as \u00A0 + return (ch === ' ' || ch === '\r' || ch === '\t' || + ch === '\n' || ch === '\v' || ch === '\u00A0'); + }, + + isIdent: function(ch) { + return ('a' <= ch && ch <= 'z' || + 'A' <= ch && ch <= 'Z' || + '_' === ch || ch === '$'); + }, + + isExpOperator: function(ch) { + return (ch === '-' || ch === '+' || this.isNumber(ch)); + }, + + throwError: function(error, start, end) { + end = end || this.index; + var colStr = (isDefined(start) + ? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']' + : ' ' + end); + throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].', + error, colStr, this.text); + }, + + readNumber: function() { + var number = ''; + var start = this.index; + while (this.index < this.text.length) { + var ch = lowercase(this.text.charAt(this.index)); + if (ch == '.' || this.isNumber(ch)) { + number += ch; + } else { + var peekCh = this.peek(); + if (ch == 'e' && this.isExpOperator(peekCh)) { + number += ch; + } else if (this.isExpOperator(ch) && + peekCh && this.isNumber(peekCh) && + number.charAt(number.length - 1) == 'e') { + number += ch; + } else if (this.isExpOperator(ch) && + (!peekCh || !this.isNumber(peekCh)) && + number.charAt(number.length - 1) == 'e') { + this.throwError('Invalid exponent'); + } else { + break; + } + } + this.index++; + } + this.tokens.push({ + index: start, + text: number, + constant: true, + value: Number(number) + }); + }, + + readIdent: function() { + var start = this.index; + while (this.index < this.text.length) { + var ch = this.text.charAt(this.index); + if (!(this.isIdent(ch) || this.isNumber(ch))) { + break; + } + this.index++; + } + this.tokens.push({ + index: start, + text: this.text.slice(start, this.index), + identifier: true + }); + }, + + readString: function(quote) { + var start = this.index; + this.index++; + var string = ''; + var rawString = quote; + var escape = false; + while (this.index < this.text.length) { + var ch = this.text.charAt(this.index); + rawString += ch; + if (escape) { + if (ch === 'u') { + var hex = this.text.substring(this.index + 1, this.index + 5); + if (!hex.match(/[\da-f]{4}/i)) { + this.throwError('Invalid unicode escape [\\u' + hex + ']'); + } + this.index += 4; + string += String.fromCharCode(parseInt(hex, 16)); + } else { + var rep = ESCAPE[ch]; + string = string + (rep || ch); + } + escape = false; + } else if (ch === '\\') { + escape = true; + } else if (ch === quote) { + this.index++; + this.tokens.push({ + index: start, + text: rawString, + constant: true, + value: string + }); + return; + } else { + string += ch; + } + this.index++; + } + this.throwError('Unterminated quote', start); + } +}; + +var AST = function(lexer, options) { + this.lexer = lexer; + this.options = options; +}; + +AST.Program = 'Program'; +AST.ExpressionStatement = 'ExpressionStatement'; +AST.AssignmentExpression = 'AssignmentExpression'; +AST.ConditionalExpression = 'ConditionalExpression'; +AST.LogicalExpression = 'LogicalExpression'; +AST.BinaryExpression = 'BinaryExpression'; +AST.UnaryExpression = 'UnaryExpression'; +AST.CallExpression = 'CallExpression'; +AST.MemberExpression = 'MemberExpression'; +AST.Identifier = 'Identifier'; +AST.Literal = 'Literal'; +AST.ArrayExpression = 'ArrayExpression'; +AST.Property = 'Property'; +AST.ObjectExpression = 'ObjectExpression'; +AST.ThisExpression = 'ThisExpression'; +AST.LocalsExpression = 'LocalsExpression'; + +// Internal use only +AST.NGValueParameter = 'NGValueParameter'; + +AST.prototype = { + ast: function(text) { + this.text = text; + this.tokens = this.lexer.lex(text); + + var value = this.program(); + + if (this.tokens.length !== 0) { + this.throwError('is an unexpected token', this.tokens[0]); + } + + return value; + }, + + program: function() { + var body = []; + while (true) { + if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']')) + body.push(this.expressionStatement()); + if (!this.expect(';')) { + return { type: AST.Program, body: body}; + } + } + }, + + expressionStatement: function() { + return { type: AST.ExpressionStatement, expression: this.filterChain() }; + }, + + filterChain: function() { + var left = this.expression(); + var token; + while ((token = this.expect('|'))) { + left = this.filter(left); + } + return left; + }, + + expression: function() { + return this.assignment(); + }, + + assignment: function() { + var result = this.ternary(); + if (this.expect('=')) { + result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='}; + } + return result; + }, + + ternary: function() { + var test = this.logicalOR(); + var alternate; + var consequent; + if (this.expect('?')) { + alternate = this.expression(); + if (this.consume(':')) { + consequent = this.expression(); + return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent}; + } + } + return test; + }, + + logicalOR: function() { + var left = this.logicalAND(); + while (this.expect('||')) { + left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() }; + } + return left; + }, + + logicalAND: function() { + var left = this.equality(); + while (this.expect('&&')) { + left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()}; + } + return left; + }, + + equality: function() { + var left = this.relational(); + var token; + while ((token = this.expect('==','!=','===','!=='))) { + left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() }; + } + return left; + }, + + relational: function() { + var left = this.additive(); + var token; + while ((token = this.expect('<', '>', '<=', '>='))) { + left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() }; + } + return left; + }, + + additive: function() { + var left = this.multiplicative(); + var token; + while ((token = this.expect('+','-'))) { + left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() }; + } + return left; + }, + + multiplicative: function() { + var left = this.unary(); + var token; + while ((token = this.expect('*','/','%'))) { + left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() }; + } + return left; + }, + + unary: function() { + var token; + if ((token = this.expect('+', '-', '!'))) { + return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() }; + } else { + return this.primary(); + } + }, + + primary: function() { + var primary; + if (this.expect('(')) { + primary = this.filterChain(); + this.consume(')'); + } else if (this.expect('[')) { + primary = this.arrayDeclaration(); + } else if (this.expect('{')) { + primary = this.object(); + } else if (this.selfReferential.hasOwnProperty(this.peek().text)) { + primary = copy(this.selfReferential[this.consume().text]); + } else if (this.options.literals.hasOwnProperty(this.peek().text)) { + primary = { type: AST.Literal, value: this.options.literals[this.consume().text]}; + } else if (this.peek().identifier) { + primary = this.identifier(); + } else if (this.peek().constant) { + primary = this.constant(); + } else { + this.throwError('not a primary expression', this.peek()); + } + + var next; + while ((next = this.expect('(', '[', '.'))) { + if (next.text === '(') { + primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() }; + this.consume(')'); + } else if (next.text === '[') { + primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true }; + this.consume(']'); + } else if (next.text === '.') { + primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false }; + } else { + this.throwError('IMPOSSIBLE'); + } + } + return primary; + }, + + filter: function(baseExpression) { + var args = [baseExpression]; + var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true}; + + while (this.expect(':')) { + args.push(this.expression()); + } + + return result; + }, + + parseArguments: function() { + var args = []; + if (this.peekToken().text !== ')') { + do { + args.push(this.expression()); + } while (this.expect(',')); + } + return args; + }, + + identifier: function() { + var token = this.consume(); + if (!token.identifier) { + this.throwError('is not a valid identifier', token); + } + return { type: AST.Identifier, name: token.text }; + }, + + constant: function() { + // TODO check that it is a constant + return { type: AST.Literal, value: this.consume().value }; + }, + + arrayDeclaration: function() { + var elements = []; + if (this.peekToken().text !== ']') { + do { + if (this.peek(']')) { + // Support trailing commas per ES5.1. + break; + } + elements.push(this.expression()); + } while (this.expect(',')); + } + this.consume(']'); + + return { type: AST.ArrayExpression, elements: elements }; + }, + + object: function() { + var properties = [], property; + if (this.peekToken().text !== '}') { + do { + if (this.peek('}')) { + // Support trailing commas per ES5.1. + break; + } + property = {type: AST.Property, kind: 'init'}; + if (this.peek().constant) { + property.key = this.constant(); + } else if (this.peek().identifier) { + property.key = this.identifier(); + } else { + this.throwError("invalid key", this.peek()); + } + this.consume(':'); + property.value = this.expression(); + properties.push(property); + } while (this.expect(',')); + } + this.consume('}'); + + return {type: AST.ObjectExpression, properties: properties }; + }, + + throwError: function(msg, token) { + throw $parseMinErr('syntax', + 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].', + token.text, msg, (token.index + 1), this.text, this.text.substring(token.index)); + }, + + consume: function(e1) { + if (this.tokens.length === 0) { + throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text); + } + + var token = this.expect(e1); + if (!token) { + this.throwError('is unexpected, expecting [' + e1 + ']', this.peek()); + } + return token; + }, + + peekToken: function() { + if (this.tokens.length === 0) { + throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text); + } + return this.tokens[0]; + }, + + peek: function(e1, e2, e3, e4) { + return this.peekAhead(0, e1, e2, e3, e4); + }, + + peekAhead: function(i, e1, e2, e3, e4) { + if (this.tokens.length > i) { + var token = this.tokens[i]; + var t = token.text; + if (t === e1 || t === e2 || t === e3 || t === e4 || + (!e1 && !e2 && !e3 && !e4)) { + return token; + } + } + return false; + }, + + expect: function(e1, e2, e3, e4) { + var token = this.peek(e1, e2, e3, e4); + if (token) { + this.tokens.shift(); + return token; + } + return false; + }, + + selfReferential: { + 'this': {type: AST.ThisExpression }, + '$locals': {type: AST.LocalsExpression } + } +}; + +function ifDefined(v, d) { + return typeof v !== 'undefined' ? v : d; +} + +function plusFn(l, r) { + if (typeof l === 'undefined') return r; + if (typeof r === 'undefined') return l; + return l + r; +} + +function isStateless($filter, filterName) { + var fn = $filter(filterName); + return !fn.$stateful; +} + +function findConstantAndWatchExpressions(ast, $filter) { + var allConstants; + var argsToWatch; + switch (ast.type) { + case AST.Program: + allConstants = true; + forEach(ast.body, function(expr) { + findConstantAndWatchExpressions(expr.expression, $filter); + allConstants = allConstants && expr.expression.constant; + }); + ast.constant = allConstants; + break; + case AST.Literal: + ast.constant = true; + ast.toWatch = []; + break; + case AST.UnaryExpression: + findConstantAndWatchExpressions(ast.argument, $filter); + ast.constant = ast.argument.constant; + ast.toWatch = ast.argument.toWatch; + break; + case AST.BinaryExpression: + findConstantAndWatchExpressions(ast.left, $filter); + findConstantAndWatchExpressions(ast.right, $filter); + ast.constant = ast.left.constant && ast.right.constant; + ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch); + break; + case AST.LogicalExpression: + findConstantAndWatchExpressions(ast.left, $filter); + findConstantAndWatchExpressions(ast.right, $filter); + ast.constant = ast.left.constant && ast.right.constant; + ast.toWatch = ast.constant ? [] : [ast]; + break; + case AST.ConditionalExpression: + findConstantAndWatchExpressions(ast.test, $filter); + findConstantAndWatchExpressions(ast.alternate, $filter); + findConstantAndWatchExpressions(ast.consequent, $filter); + ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant; + ast.toWatch = ast.constant ? [] : [ast]; + break; + case AST.Identifier: + ast.constant = false; + ast.toWatch = [ast]; + break; + case AST.MemberExpression: + findConstantAndWatchExpressions(ast.object, $filter); + if (ast.computed) { + findConstantAndWatchExpressions(ast.property, $filter); + } + ast.constant = ast.object.constant && (!ast.computed || ast.property.constant); + ast.toWatch = [ast]; + break; + case AST.CallExpression: + allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false; + argsToWatch = []; + forEach(ast.arguments, function(expr) { + findConstantAndWatchExpressions(expr, $filter); + allConstants = allConstants && expr.constant; + if (!expr.constant) { + argsToWatch.push.apply(argsToWatch, expr.toWatch); + } + }); + ast.constant = allConstants; + ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast]; + break; + case AST.AssignmentExpression: + findConstantAndWatchExpressions(ast.left, $filter); + findConstantAndWatchExpressions(ast.right, $filter); + ast.constant = ast.left.constant && ast.right.constant; + ast.toWatch = [ast]; + break; + case AST.ArrayExpression: + allConstants = true; + argsToWatch = []; + forEach(ast.elements, function(expr) { + findConstantAndWatchExpressions(expr, $filter); + allConstants = allConstants && expr.constant; + if (!expr.constant) { + argsToWatch.push.apply(argsToWatch, expr.toWatch); + } + }); + ast.constant = allConstants; + ast.toWatch = argsToWatch; + break; + case AST.ObjectExpression: + allConstants = true; + argsToWatch = []; + forEach(ast.properties, function(property) { + findConstantAndWatchExpressions(property.value, $filter); + allConstants = allConstants && property.value.constant; + if (!property.value.constant) { + argsToWatch.push.apply(argsToWatch, property.value.toWatch); + } + }); + ast.constant = allConstants; + ast.toWatch = argsToWatch; + break; + case AST.ThisExpression: + ast.constant = false; + ast.toWatch = []; + break; + case AST.LocalsExpression: + ast.constant = false; + ast.toWatch = []; + break; + } +} + +function getInputs(body) { + if (body.length != 1) return; + var lastExpression = body[0].expression; + var candidate = lastExpression.toWatch; + if (candidate.length !== 1) return candidate; + return candidate[0] !== lastExpression ? candidate : undefined; +} + +function isAssignable(ast) { + return ast.type === AST.Identifier || ast.type === AST.MemberExpression; +} + +function assignableAST(ast) { + if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) { + return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='}; + } +} + +function isLiteral(ast) { + return ast.body.length === 0 || + ast.body.length === 1 && ( + ast.body[0].expression.type === AST.Literal || + ast.body[0].expression.type === AST.ArrayExpression || + ast.body[0].expression.type === AST.ObjectExpression); +} + +function isConstant(ast) { + return ast.constant; +} + +function ASTCompiler(astBuilder, $filter) { + this.astBuilder = astBuilder; + this.$filter = $filter; +} + +ASTCompiler.prototype = { + compile: function(expression, expensiveChecks) { + var self = this; + var ast = this.astBuilder.ast(expression); + this.state = { + nextId: 0, + filters: {}, + expensiveChecks: expensiveChecks, + fn: {vars: [], body: [], own: {}}, + assign: {vars: [], body: [], own: {}}, + inputs: [] + }; + findConstantAndWatchExpressions(ast, self.$filter); + var extra = ''; + var assignable; + this.stage = 'assign'; + if ((assignable = assignableAST(ast))) { + this.state.computing = 'assign'; + var result = this.nextId(); + this.recurse(assignable, result); + this.return_(result); + extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l'); + } + var toWatch = getInputs(ast.body); + self.stage = 'inputs'; + forEach(toWatch, function(watch, key) { + var fnKey = 'fn' + key; + self.state[fnKey] = {vars: [], body: [], own: {}}; + self.state.computing = fnKey; + var intoId = self.nextId(); + self.recurse(watch, intoId); + self.return_(intoId); + self.state.inputs.push(fnKey); + watch.watchId = key; + }); + this.state.computing = 'fn'; + this.stage = 'main'; + this.recurse(ast); + var fnString = + // The build and minification steps remove the string "use strict" from the code, but this is done using a regex. + // This is a workaround for this until we do a better job at only removing the prefix only when we should. + '"' + this.USE + ' ' + this.STRICT + '";\n' + + this.filterPrefix() + + 'var fn=' + this.generateFunction('fn', 's,l,a,i') + + extra + + this.watchFns() + + 'return fn;'; + + /* jshint -W054 */ + var fn = (new Function('$filter', + 'ensureSafeMemberName', + 'ensureSafeObject', + 'ensureSafeFunction', + 'getStringValue', + 'ensureSafeAssignContext', + 'ifDefined', + 'plus', + 'text', + fnString))( + this.$filter, + ensureSafeMemberName, + ensureSafeObject, + ensureSafeFunction, + getStringValue, + ensureSafeAssignContext, + ifDefined, + plusFn, + expression); + /* jshint +W054 */ + this.state = this.stage = undefined; + fn.literal = isLiteral(ast); + fn.constant = isConstant(ast); + return fn; + }, + + USE: 'use', + + STRICT: 'strict', + + watchFns: function() { + var result = []; + var fns = this.state.inputs; + var self = this; + forEach(fns, function(name) { + result.push('var ' + name + '=' + self.generateFunction(name, 's')); + }); + if (fns.length) { + result.push('fn.inputs=[' + fns.join(',') + '];'); + } + return result.join(''); + }, + + generateFunction: function(name, params) { + return 'function(' + params + '){' + + this.varsPrefix(name) + + this.body(name) + + '};'; + }, + + filterPrefix: function() { + var parts = []; + var self = this; + forEach(this.state.filters, function(id, filter) { + parts.push(id + '=$filter(' + self.escape(filter) + ')'); + }); + if (parts.length) return 'var ' + parts.join(',') + ';'; + return ''; + }, + + varsPrefix: function(section) { + return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : ''; + }, + + body: function(section) { + return this.state[section].body.join(''); + }, + + recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) { + var left, right, self = this, args, expression; + recursionFn = recursionFn || noop; + if (!skipWatchIdCheck && isDefined(ast.watchId)) { + intoId = intoId || this.nextId(); + this.if_('i', + this.lazyAssign(intoId, this.computedMember('i', ast.watchId)), + this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true) + ); + return; + } + switch (ast.type) { + case AST.Program: + forEach(ast.body, function(expression, pos) { + self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; }); + if (pos !== ast.body.length - 1) { + self.current().body.push(right, ';'); + } else { + self.return_(right); + } + }); + break; + case AST.Literal: + expression = this.escape(ast.value); + this.assign(intoId, expression); + recursionFn(expression); + break; + case AST.UnaryExpression: + this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; }); + expression = ast.operator + '(' + this.ifDefined(right, 0) + ')'; + this.assign(intoId, expression); + recursionFn(expression); + break; + case AST.BinaryExpression: + this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; }); + this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; }); + if (ast.operator === '+') { + expression = this.plus(left, right); + } else if (ast.operator === '-') { + expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0); + } else { + expression = '(' + left + ')' + ast.operator + '(' + right + ')'; + } + this.assign(intoId, expression); + recursionFn(expression); + break; + case AST.LogicalExpression: + intoId = intoId || this.nextId(); + self.recurse(ast.left, intoId); + self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId)); + recursionFn(intoId); + break; + case AST.ConditionalExpression: + intoId = intoId || this.nextId(); + self.recurse(ast.test, intoId); + self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId)); + recursionFn(intoId); + break; + case AST.Identifier: + intoId = intoId || this.nextId(); + if (nameId) { + nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s'); + nameId.computed = false; + nameId.name = ast.name; + } + ensureSafeMemberName(ast.name); + self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)), + function() { + self.if_(self.stage === 'inputs' || 's', function() { + if (create && create !== 1) { + self.if_( + self.not(self.nonComputedMember('s', ast.name)), + self.lazyAssign(self.nonComputedMember('s', ast.name), '{}')); + } + self.assign(intoId, self.nonComputedMember('s', ast.name)); + }); + }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name)) + ); + if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) { + self.addEnsureSafeObject(intoId); + } + recursionFn(intoId); + break; + case AST.MemberExpression: + left = nameId && (nameId.context = this.nextId()) || this.nextId(); + intoId = intoId || this.nextId(); + self.recurse(ast.object, left, undefined, function() { + self.if_(self.notNull(left), function() { + if (create && create !== 1) { + self.addEnsureSafeAssignContext(left); + } + if (ast.computed) { + right = self.nextId(); + self.recurse(ast.property, right); + self.getStringValue(right); + self.addEnsureSafeMemberName(right); + if (create && create !== 1) { + self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}')); + } + expression = self.ensureSafeObject(self.computedMember(left, right)); + self.assign(intoId, expression); + if (nameId) { + nameId.computed = true; + nameId.name = right; + } + } else { + ensureSafeMemberName(ast.property.name); + if (create && create !== 1) { + self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}')); + } + expression = self.nonComputedMember(left, ast.property.name); + if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) { + expression = self.ensureSafeObject(expression); + } + self.assign(intoId, expression); + if (nameId) { + nameId.computed = false; + nameId.name = ast.property.name; + } + } + }, function() { + self.assign(intoId, 'undefined'); + }); + recursionFn(intoId); + }, !!create); + break; + case AST.CallExpression: + intoId = intoId || this.nextId(); + if (ast.filter) { + right = self.filter(ast.callee.name); + args = []; + forEach(ast.arguments, function(expr) { + var argument = self.nextId(); + self.recurse(expr, argument); + args.push(argument); + }); + expression = right + '(' + args.join(',') + ')'; + self.assign(intoId, expression); + recursionFn(intoId); + } else { + right = self.nextId(); + left = {}; + args = []; + self.recurse(ast.callee, right, left, function() { + self.if_(self.notNull(right), function() { + self.addEnsureSafeFunction(right); + forEach(ast.arguments, function(expr) { + self.recurse(expr, self.nextId(), undefined, function(argument) { + args.push(self.ensureSafeObject(argument)); + }); + }); + if (left.name) { + if (!self.state.expensiveChecks) { + self.addEnsureSafeObject(left.context); + } + expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')'; + } else { + expression = right + '(' + args.join(',') + ')'; + } + expression = self.ensureSafeObject(expression); + self.assign(intoId, expression); + }, function() { + self.assign(intoId, 'undefined'); + }); + recursionFn(intoId); + }); + } + break; + case AST.AssignmentExpression: + right = this.nextId(); + left = {}; + if (!isAssignable(ast.left)) { + throw $parseMinErr('lval', 'Trying to assign a value to a non l-value'); + } + this.recurse(ast.left, undefined, left, function() { + self.if_(self.notNull(left.context), function() { + self.recurse(ast.right, right); + self.addEnsureSafeObject(self.member(left.context, left.name, left.computed)); + self.addEnsureSafeAssignContext(left.context); + expression = self.member(left.context, left.name, left.computed) + ast.operator + right; + self.assign(intoId, expression); + recursionFn(intoId || expression); + }); + }, 1); + break; + case AST.ArrayExpression: + args = []; + forEach(ast.elements, function(expr) { + self.recurse(expr, self.nextId(), undefined, function(argument) { + args.push(argument); + }); + }); + expression = '[' + args.join(',') + ']'; + this.assign(intoId, expression); + recursionFn(expression); + break; + case AST.ObjectExpression: + args = []; + forEach(ast.properties, function(property) { + self.recurse(property.value, self.nextId(), undefined, function(expr) { + args.push(self.escape( + property.key.type === AST.Identifier ? property.key.name : + ('' + property.key.value)) + + ':' + expr); + }); + }); + expression = '{' + args.join(',') + '}'; + this.assign(intoId, expression); + recursionFn(expression); + break; + case AST.ThisExpression: + this.assign(intoId, 's'); + recursionFn('s'); + break; + case AST.LocalsExpression: + this.assign(intoId, 'l'); + recursionFn('l'); + break; + case AST.NGValueParameter: + this.assign(intoId, 'v'); + recursionFn('v'); + break; + } + }, + + getHasOwnProperty: function(element, property) { + var key = element + '.' + property; + var own = this.current().own; + if (!own.hasOwnProperty(key)) { + own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')'); + } + return own[key]; + }, + + assign: function(id, value) { + if (!id) return; + this.current().body.push(id, '=', value, ';'); + return id; + }, + + filter: function(filterName) { + if (!this.state.filters.hasOwnProperty(filterName)) { + this.state.filters[filterName] = this.nextId(true); + } + return this.state.filters[filterName]; + }, + + ifDefined: function(id, defaultValue) { + return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')'; + }, + + plus: function(left, right) { + return 'plus(' + left + ',' + right + ')'; + }, + + return_: function(id) { + this.current().body.push('return ', id, ';'); + }, + + if_: function(test, alternate, consequent) { + if (test === true) { + alternate(); + } else { + var body = this.current().body; + body.push('if(', test, '){'); + alternate(); + body.push('}'); + if (consequent) { + body.push('else{'); + consequent(); + body.push('}'); + } + } + }, + + not: function(expression) { + return '!(' + expression + ')'; + }, + + notNull: function(expression) { + return expression + '!=null'; + }, + + nonComputedMember: function(left, right) { + return left + '.' + right; + }, + + computedMember: function(left, right) { + return left + '[' + right + ']'; + }, + + member: function(left, right, computed) { + if (computed) return this.computedMember(left, right); + return this.nonComputedMember(left, right); + }, + + addEnsureSafeObject: function(item) { + this.current().body.push(this.ensureSafeObject(item), ';'); + }, + + addEnsureSafeMemberName: function(item) { + this.current().body.push(this.ensureSafeMemberName(item), ';'); + }, + + addEnsureSafeFunction: function(item) { + this.current().body.push(this.ensureSafeFunction(item), ';'); + }, + + addEnsureSafeAssignContext: function(item) { + this.current().body.push(this.ensureSafeAssignContext(item), ';'); + }, + + ensureSafeObject: function(item) { + return 'ensureSafeObject(' + item + ',text)'; + }, + + ensureSafeMemberName: function(item) { + return 'ensureSafeMemberName(' + item + ',text)'; + }, + + ensureSafeFunction: function(item) { + return 'ensureSafeFunction(' + item + ',text)'; + }, + + getStringValue: function(item) { + this.assign(item, 'getStringValue(' + item + ')'); + }, + + ensureSafeAssignContext: function(item) { + return 'ensureSafeAssignContext(' + item + ',text)'; + }, + + lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) { + var self = this; + return function() { + self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck); + }; + }, + + lazyAssign: function(id, value) { + var self = this; + return function() { + self.assign(id, value); + }; + }, + + stringEscapeRegex: /[^ a-zA-Z0-9]/g, + + stringEscapeFn: function(c) { + return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4); + }, + + escape: function(value) { + if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'"; + if (isNumber(value)) return value.toString(); + if (value === true) return 'true'; + if (value === false) return 'false'; + if (value === null) return 'null'; + if (typeof value === 'undefined') return 'undefined'; + + throw $parseMinErr('esc', 'IMPOSSIBLE'); + }, + + nextId: function(skip, init) { + var id = 'v' + (this.state.nextId++); + if (!skip) { + this.current().vars.push(id + (init ? '=' + init : '')); + } + return id; + }, + + current: function() { + return this.state[this.state.computing]; + } +}; + + +function ASTInterpreter(astBuilder, $filter) { + this.astBuilder = astBuilder; + this.$filter = $filter; +} + +ASTInterpreter.prototype = { + compile: function(expression, expensiveChecks) { + var self = this; + var ast = this.astBuilder.ast(expression); + this.expression = expression; + this.expensiveChecks = expensiveChecks; + findConstantAndWatchExpressions(ast, self.$filter); + var assignable; + var assign; + if ((assignable = assignableAST(ast))) { + assign = this.recurse(assignable); + } + var toWatch = getInputs(ast.body); + var inputs; + if (toWatch) { + inputs = []; + forEach(toWatch, function(watch, key) { + var input = self.recurse(watch); + watch.input = input; + inputs.push(input); + watch.watchId = key; + }); + } + var expressions = []; + forEach(ast.body, function(expression) { + expressions.push(self.recurse(expression.expression)); + }); + var fn = ast.body.length === 0 ? noop : + ast.body.length === 1 ? expressions[0] : + function(scope, locals) { + var lastValue; + forEach(expressions, function(exp) { + lastValue = exp(scope, locals); + }); + return lastValue; + }; + if (assign) { + fn.assign = function(scope, value, locals) { + return assign(scope, locals, value); + }; + } + if (inputs) { + fn.inputs = inputs; + } + fn.literal = isLiteral(ast); + fn.constant = isConstant(ast); + return fn; + }, + + recurse: function(ast, context, create) { + var left, right, self = this, args, expression; + if (ast.input) { + return this.inputs(ast.input, ast.watchId); + } + switch (ast.type) { + case AST.Literal: + return this.value(ast.value, context); + case AST.UnaryExpression: + right = this.recurse(ast.argument); + return this['unary' + ast.operator](right, context); + case AST.BinaryExpression: + left = this.recurse(ast.left); + right = this.recurse(ast.right); + return this['binary' + ast.operator](left, right, context); + case AST.LogicalExpression: + left = this.recurse(ast.left); + right = this.recurse(ast.right); + return this['binary' + ast.operator](left, right, context); + case AST.ConditionalExpression: + return this['ternary?:']( + this.recurse(ast.test), + this.recurse(ast.alternate), + this.recurse(ast.consequent), + context + ); + case AST.Identifier: + ensureSafeMemberName(ast.name, self.expression); + return self.identifier(ast.name, + self.expensiveChecks || isPossiblyDangerousMemberName(ast.name), + context, create, self.expression); + case AST.MemberExpression: + left = this.recurse(ast.object, false, !!create); + if (!ast.computed) { + ensureSafeMemberName(ast.property.name, self.expression); + right = ast.property.name; + } + if (ast.computed) right = this.recurse(ast.property); + return ast.computed ? + this.computedMember(left, right, context, create, self.expression) : + this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression); + case AST.CallExpression: + args = []; + forEach(ast.arguments, function(expr) { + args.push(self.recurse(expr)); + }); + if (ast.filter) right = this.$filter(ast.callee.name); + if (!ast.filter) right = this.recurse(ast.callee, true); + return ast.filter ? + function(scope, locals, assign, inputs) { + var values = []; + for (var i = 0; i < args.length; ++i) { + values.push(args[i](scope, locals, assign, inputs)); + } + var value = right.apply(undefined, values, inputs); + return context ? {context: undefined, name: undefined, value: value} : value; + } : + function(scope, locals, assign, inputs) { + var rhs = right(scope, locals, assign, inputs); + var value; + if (rhs.value != null) { + ensureSafeObject(rhs.context, self.expression); + ensureSafeFunction(rhs.value, self.expression); + var values = []; + for (var i = 0; i < args.length; ++i) { + values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression)); + } + value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression); + } + return context ? {value: value} : value; + }; + case AST.AssignmentExpression: + left = this.recurse(ast.left, true, 1); + right = this.recurse(ast.right); + return function(scope, locals, assign, inputs) { + var lhs = left(scope, locals, assign, inputs); + var rhs = right(scope, locals, assign, inputs); + ensureSafeObject(lhs.value, self.expression); + ensureSafeAssignContext(lhs.context); + lhs.context[lhs.name] = rhs; + return context ? {value: rhs} : rhs; + }; + case AST.ArrayExpression: + args = []; + forEach(ast.elements, function(expr) { + args.push(self.recurse(expr)); + }); + return function(scope, locals, assign, inputs) { + var value = []; + for (var i = 0; i < args.length; ++i) { + value.push(args[i](scope, locals, assign, inputs)); + } + return context ? {value: value} : value; + }; + case AST.ObjectExpression: + args = []; + forEach(ast.properties, function(property) { + args.push({key: property.key.type === AST.Identifier ? + property.key.name : + ('' + property.key.value), + value: self.recurse(property.value) + }); + }); + return function(scope, locals, assign, inputs) { + var value = {}; + for (var i = 0; i < args.length; ++i) { + value[args[i].key] = args[i].value(scope, locals, assign, inputs); + } + return context ? {value: value} : value; + }; + case AST.ThisExpression: + return function(scope) { + return context ? {value: scope} : scope; + }; + case AST.LocalsExpression: + return function(scope, locals) { + return context ? {value: locals} : locals; + }; + case AST.NGValueParameter: + return function(scope, locals, assign) { + return context ? {value: assign} : assign; + }; + } + }, + + 'unary+': function(argument, context) { + return function(scope, locals, assign, inputs) { + var arg = argument(scope, locals, assign, inputs); + if (isDefined(arg)) { + arg = +arg; + } else { + arg = 0; + } + return context ? {value: arg} : arg; + }; + }, + 'unary-': function(argument, context) { + return function(scope, locals, assign, inputs) { + var arg = argument(scope, locals, assign, inputs); + if (isDefined(arg)) { + arg = -arg; + } else { + arg = 0; + } + return context ? {value: arg} : arg; + }; + }, + 'unary!': function(argument, context) { + return function(scope, locals, assign, inputs) { + var arg = !argument(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary+': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var lhs = left(scope, locals, assign, inputs); + var rhs = right(scope, locals, assign, inputs); + var arg = plusFn(lhs, rhs); + return context ? {value: arg} : arg; + }; + }, + 'binary-': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var lhs = left(scope, locals, assign, inputs); + var rhs = right(scope, locals, assign, inputs); + var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0); + return context ? {value: arg} : arg; + }; + }, + 'binary*': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary/': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary%': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary===': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary!==': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary==': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary!=': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary<': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary>': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary<=': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary>=': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary&&': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary||': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'ternary?:': function(test, alternate, consequent, context) { + return function(scope, locals, assign, inputs) { + var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + value: function(value, context) { + return function() { return context ? {context: undefined, name: undefined, value: value} : value; }; + }, + identifier: function(name, expensiveChecks, context, create, expression) { + return function(scope, locals, assign, inputs) { + var base = locals && (name in locals) ? locals : scope; + if (create && create !== 1 && base && !(base[name])) { + base[name] = {}; + } + var value = base ? base[name] : undefined; + if (expensiveChecks) { + ensureSafeObject(value, expression); + } + if (context) { + return {context: base, name: name, value: value}; + } else { + return value; + } + }; + }, + computedMember: function(left, right, context, create, expression) { + return function(scope, locals, assign, inputs) { + var lhs = left(scope, locals, assign, inputs); + var rhs; + var value; + if (lhs != null) { + rhs = right(scope, locals, assign, inputs); + rhs = getStringValue(rhs); + ensureSafeMemberName(rhs, expression); + if (create && create !== 1) { + ensureSafeAssignContext(lhs); + if (lhs && !(lhs[rhs])) { + lhs[rhs] = {}; + } + } + value = lhs[rhs]; + ensureSafeObject(value, expression); + } + if (context) { + return {context: lhs, name: rhs, value: value}; + } else { + return value; + } + }; + }, + nonComputedMember: function(left, right, expensiveChecks, context, create, expression) { + return function(scope, locals, assign, inputs) { + var lhs = left(scope, locals, assign, inputs); + if (create && create !== 1) { + ensureSafeAssignContext(lhs); + if (lhs && !(lhs[right])) { + lhs[right] = {}; + } + } + var value = lhs != null ? lhs[right] : undefined; + if (expensiveChecks || isPossiblyDangerousMemberName(right)) { + ensureSafeObject(value, expression); + } + if (context) { + return {context: lhs, name: right, value: value}; + } else { + return value; + } + }; + }, + inputs: function(input, watchId) { + return function(scope, value, locals, inputs) { + if (inputs) return inputs[watchId]; + return input(scope, value, locals); + }; + } +}; + +/** + * @constructor + */ +var Parser = function(lexer, $filter, options) { + this.lexer = lexer; + this.$filter = $filter; + this.options = options; + this.ast = new AST(lexer, options); + this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) : + new ASTCompiler(this.ast, $filter); +}; + +Parser.prototype = { + constructor: Parser, + + parse: function(text) { + return this.astCompiler.compile(text, this.options.expensiveChecks); + } +}; + +function isPossiblyDangerousMemberName(name) { + return name == 'constructor'; +} + +var objectValueOf = Object.prototype.valueOf; + +function getValueOf(value) { + return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value); +} + +/////////////////////////////////// + +/** + * @ngdoc service + * @name $parse + * @kind function + * + * @description + * + * Converts Angular {@link guide/expression expression} into a function. + * + * ```js + * var getter = $parse('user.name'); + * var setter = getter.assign; + * var context = {user:{name:'angular'}}; + * var locals = {user:{name:'local'}}; + * + * expect(getter(context)).toEqual('angular'); + * setter(context, 'newValue'); + * expect(context.user.name).toEqual('newValue'); + * expect(getter(context, locals)).toEqual('local'); + * ``` + * + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + * + * The returned function also has the following properties: + * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript + * literal. + * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript + * constant literals. + * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be + * set to a function to change its value on the given context. + * + */ + + +/** + * @ngdoc provider + * @name $parseProvider + * + * @description + * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse} + * service. + */ +function $ParseProvider() { + var cacheDefault = createMap(); + var cacheExpensive = createMap(); + var literals = { + 'true': true, + 'false': false, + 'null': null, + 'undefined': undefined + }; + + /** + * @ngdoc method + * @name $parseProvider#addLiteral + * @description + * + * Configure $parse service to add literal values that will be present as literal at expressions. + * + * @param {string} literalName Token for the literal value. The literal name value must be a valid literal name. + * @param {*} literalValue Value for this literal. All literal values must be primitives or `undefined`. + * + **/ + this.addLiteral = function(literalName, literalValue) { + literals[literalName] = literalValue; + }; + + this.$get = ['$filter', function($filter) { + var noUnsafeEval = csp().noUnsafeEval; + var $parseOptions = { + csp: noUnsafeEval, + expensiveChecks: false, + literals: copy(literals) + }, + $parseOptionsExpensive = { + csp: noUnsafeEval, + expensiveChecks: true, + literals: copy(literals) + }; + var runningChecksEnabled = false; + + $parse.$$runningExpensiveChecks = function() { + return runningChecksEnabled; + }; + + return $parse; + + function $parse(exp, interceptorFn, expensiveChecks) { + var parsedExpression, oneTime, cacheKey; + + expensiveChecks = expensiveChecks || runningChecksEnabled; + + switch (typeof exp) { + case 'string': + exp = exp.trim(); + cacheKey = exp; + + var cache = (expensiveChecks ? cacheExpensive : cacheDefault); + parsedExpression = cache[cacheKey]; + + if (!parsedExpression) { + if (exp.charAt(0) === ':' && exp.charAt(1) === ':') { + oneTime = true; + exp = exp.substring(2); + } + var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions; + var lexer = new Lexer(parseOptions); + var parser = new Parser(lexer, $filter, parseOptions); + parsedExpression = parser.parse(exp); + if (parsedExpression.constant) { + parsedExpression.$$watchDelegate = constantWatchDelegate; + } else if (oneTime) { + parsedExpression.$$watchDelegate = parsedExpression.literal ? + oneTimeLiteralWatchDelegate : oneTimeWatchDelegate; + } else if (parsedExpression.inputs) { + parsedExpression.$$watchDelegate = inputsWatchDelegate; + } + if (expensiveChecks) { + parsedExpression = expensiveChecksInterceptor(parsedExpression); + } + cache[cacheKey] = parsedExpression; + } + return addInterceptor(parsedExpression, interceptorFn); + + case 'function': + return addInterceptor(exp, interceptorFn); + + default: + return addInterceptor(noop, interceptorFn); + } + } + + function expensiveChecksInterceptor(fn) { + if (!fn) return fn; + expensiveCheckFn.$$watchDelegate = fn.$$watchDelegate; + expensiveCheckFn.assign = expensiveChecksInterceptor(fn.assign); + expensiveCheckFn.constant = fn.constant; + expensiveCheckFn.literal = fn.literal; + for (var i = 0; fn.inputs && i < fn.inputs.length; ++i) { + fn.inputs[i] = expensiveChecksInterceptor(fn.inputs[i]); + } + expensiveCheckFn.inputs = fn.inputs; + + return expensiveCheckFn; + + function expensiveCheckFn(scope, locals, assign, inputs) { + var expensiveCheckOldValue = runningChecksEnabled; + runningChecksEnabled = true; + try { + return fn(scope, locals, assign, inputs); + } finally { + runningChecksEnabled = expensiveCheckOldValue; + } + } + } + + function expressionInputDirtyCheck(newValue, oldValueOfValue) { + + if (newValue == null || oldValueOfValue == null) { // null/undefined + return newValue === oldValueOfValue; + } + + if (typeof newValue === 'object') { + + // attempt to convert the value to a primitive type + // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can + // be cheaply dirty-checked + newValue = getValueOf(newValue); + + if (typeof newValue === 'object') { + // objects/arrays are not supported - deep-watching them would be too expensive + return false; + } + + // fall-through to the primitive equality check + } + + //Primitive or NaN + return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue); + } + + function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) { + var inputExpressions = parsedExpression.inputs; + var lastResult; + + if (inputExpressions.length === 1) { + var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails + inputExpressions = inputExpressions[0]; + return scope.$watch(function expressionInputWatch(scope) { + var newInputValue = inputExpressions(scope); + if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) { + lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]); + oldInputValueOf = newInputValue && getValueOf(newInputValue); + } + return lastResult; + }, listener, objectEquality, prettyPrintExpression); + } + + var oldInputValueOfValues = []; + var oldInputValues = []; + for (var i = 0, ii = inputExpressions.length; i < ii; i++) { + oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails + oldInputValues[i] = null; + } + + return scope.$watch(function expressionInputsWatch(scope) { + var changed = false; + + for (var i = 0, ii = inputExpressions.length; i < ii; i++) { + var newInputValue = inputExpressions[i](scope); + if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) { + oldInputValues[i] = newInputValue; + oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue); + } + } + + if (changed) { + lastResult = parsedExpression(scope, undefined, undefined, oldInputValues); + } + + return lastResult; + }, listener, objectEquality, prettyPrintExpression); + } + + function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) { + var unwatch, lastValue; + return unwatch = scope.$watch(function oneTimeWatch(scope) { + return parsedExpression(scope); + }, function oneTimeListener(value, old, scope) { + lastValue = value; + if (isFunction(listener)) { + listener.apply(this, arguments); + } + if (isDefined(value)) { + scope.$$postDigest(function() { + if (isDefined(lastValue)) { + unwatch(); + } + }); + } + }, objectEquality); + } + + function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) { + var unwatch, lastValue; + return unwatch = scope.$watch(function oneTimeWatch(scope) { + return parsedExpression(scope); + }, function oneTimeListener(value, old, scope) { + lastValue = value; + if (isFunction(listener)) { + listener.call(this, value, old, scope); + } + if (isAllDefined(value)) { + scope.$$postDigest(function() { + if (isAllDefined(lastValue)) unwatch(); + }); + } + }, objectEquality); + + function isAllDefined(value) { + var allDefined = true; + forEach(value, function(val) { + if (!isDefined(val)) allDefined = false; + }); + return allDefined; + } + } + + function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) { + var unwatch; + return unwatch = scope.$watch(function constantWatch(scope) { + unwatch(); + return parsedExpression(scope); + }, listener, objectEquality); + } + + function addInterceptor(parsedExpression, interceptorFn) { + if (!interceptorFn) return parsedExpression; + var watchDelegate = parsedExpression.$$watchDelegate; + var useInputs = false; + + var regularWatch = + watchDelegate !== oneTimeLiteralWatchDelegate && + watchDelegate !== oneTimeWatchDelegate; + + var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) { + var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs); + return interceptorFn(value, scope, locals); + } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) { + var value = parsedExpression(scope, locals, assign, inputs); + var result = interceptorFn(value, scope, locals); + // we only return the interceptor's result if the + // initial value is defined (for bind-once) + return isDefined(value) ? result : value; + }; + + // Propagate $$watchDelegates other then inputsWatchDelegate + if (parsedExpression.$$watchDelegate && + parsedExpression.$$watchDelegate !== inputsWatchDelegate) { + fn.$$watchDelegate = parsedExpression.$$watchDelegate; + } else if (!interceptorFn.$stateful) { + // If there is an interceptor, but no watchDelegate then treat the interceptor like + // we treat filters - it is assumed to be a pure function unless flagged with $stateful + fn.$$watchDelegate = inputsWatchDelegate; + useInputs = !parsedExpression.inputs; + fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression]; + } + + return fn; + } + }]; +} + +/** + * @ngdoc service + * @name $q + * @requires $rootScope + * + * @description + * A service that helps you run functions asynchronously, and use their return values (or exceptions) + * when they are done processing. + * + * This is an implementation of promises/deferred objects inspired by + * [Kris Kowal's Q](https://github.com/kriskowal/q). + * + * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred + * implementations, and the other which resembles ES6 (ES2015) promises to some degree. + * + * # $q constructor + * + * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver` + * function as the first argument. This is similar to the native Promise implementation from ES6, + * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). + * + * While the constructor-style use is supported, not all of the supporting methods from ES6 promises are + * available yet. + * + * It can be used like so: + * + * ```js + * // for the purpose of this example let's assume that variables `$q` and `okToGreet` + * // are available in the current lexical scope (they could have been injected or passed in). + * + * function asyncGreet(name) { + * // perform some asynchronous operation, resolve or reject the promise when appropriate. + * return $q(function(resolve, reject) { + * setTimeout(function() { + * if (okToGreet(name)) { + * resolve('Hello, ' + name + '!'); + * } else { + * reject('Greeting ' + name + ' is not allowed.'); + * } + * }, 1000); + * }); + * } + * + * var promise = asyncGreet('Robin Hood'); + * promise.then(function(greeting) { + * alert('Success: ' + greeting); + * }, function(reason) { + * alert('Failed: ' + reason); + * }); + * ``` + * + * Note: progress/notify callbacks are not currently supported via the ES6-style interface. + * + * Note: unlike ES6 behavior, an exception thrown in the constructor function will NOT implicitly reject the promise. + * + * However, the more traditional CommonJS-style usage is still available, and documented below. + * + * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an + * interface for interacting with an object that represents the result of an action that is + * performed asynchronously, and may or may not be finished at any given point in time. + * + * From the perspective of dealing with error handling, deferred and promise APIs are to + * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming. + * + * ```js + * // for the purpose of this example let's assume that variables `$q` and `okToGreet` + * // are available in the current lexical scope (they could have been injected or passed in). + * + * function asyncGreet(name) { + * var deferred = $q.defer(); + * + * setTimeout(function() { + * deferred.notify('About to greet ' + name + '.'); + * + * if (okToGreet(name)) { + * deferred.resolve('Hello, ' + name + '!'); + * } else { + * deferred.reject('Greeting ' + name + ' is not allowed.'); + * } + * }, 1000); + * + * return deferred.promise; + * } + * + * var promise = asyncGreet('Robin Hood'); + * promise.then(function(greeting) { + * alert('Success: ' + greeting); + * }, function(reason) { + * alert('Failed: ' + reason); + * }, function(update) { + * alert('Got notification: ' + update); + * }); + * ``` + * + * At first it might not be obvious why this extra complexity is worth the trouble. The payoff + * comes in the way of guarantees that promise and deferred APIs make, see + * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md. + * + * Additionally the promise api allows for composition that is very hard to do with the + * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach. + * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the + * section on serial or parallel joining of promises. + * + * # The Deferred API + * + * A new instance of deferred is constructed by calling `$q.defer()`. + * + * The purpose of the deferred object is to expose the associated Promise instance as well as APIs + * that can be used for signaling the successful or unsuccessful completion, as well as the status + * of the task. + * + * **Methods** + * + * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection + * constructed via `$q.reject`, the promise will be rejected instead. + * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to + * resolving it with a rejection constructed via `$q.reject`. + * - `notify(value)` - provides updates on the status of the promise's execution. This may be called + * multiple times before the promise is either resolved or rejected. + * + * **Properties** + * + * - promise – `{Promise}` – promise object associated with this deferred. + * + * + * # The Promise API + * + * A new promise instance is created when a deferred instance is created and can be retrieved by + * calling `deferred.promise`. + * + * The purpose of the promise object is to allow for interested parties to get access to the result + * of the deferred task when it completes. + * + * **Methods** + * + * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or + * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously + * as soon as the result is available. The callbacks are called with a single argument: the result + * or rejection reason. Additionally, the notify callback may be called zero or more times to + * provide a progress indication, before the promise is resolved or rejected. + * + * This method *returns a new promise* which is resolved or rejected via the return value of the + * `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved + * with the value which is resolved in that promise using + * [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)). + * It also notifies via the return value of the `notifyCallback` method. The promise cannot be + * resolved or rejected from the notifyCallback method. + * + * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)` + * + * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise, + * but to do so without modifying the final value. This is useful to release resources or do some + * clean-up that needs to be done whether the promise was rejected or resolved. See the [full + * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for + * more information. + * + * # Chaining promises + * + * Because calling the `then` method of a promise returns a new derived promise, it is easily + * possible to create a chain of promises: + * + * ```js + * promiseB = promiseA.then(function(result) { + * return result + 1; + * }); + * + * // promiseB will be resolved immediately after promiseA is resolved and its value + * // will be the result of promiseA incremented by 1 + * ``` + * + * It is possible to create chains of any length and since a promise can be resolved with another + * promise (which will defer its resolution further), it is possible to pause/defer resolution of + * the promises at any point in the chain. This makes it possible to implement powerful APIs like + * $http's response interceptors. + * + * + * # Differences between Kris Kowal's Q and $q + * + * There are two main differences: + * + * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation + * mechanism in angular, which means faster propagation of resolution or rejection into your + * models and avoiding unnecessary browser repaints, which would result in flickering UI. + * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains + * all the important functionality needed for common async tasks. + * + * # Testing + * + * ```js + * it('should simulate promise', inject(function($q, $rootScope) { + * var deferred = $q.defer(); + * var promise = deferred.promise; + * var resolvedValue; + * + * promise.then(function(value) { resolvedValue = value; }); + * expect(resolvedValue).toBeUndefined(); + * + * // Simulate resolving of promise + * deferred.resolve(123); + * // Note that the 'then' function does not get called synchronously. + * // This is because we want the promise API to always be async, whether or not + * // it got called synchronously or asynchronously. + * expect(resolvedValue).toBeUndefined(); + * + * // Propagate promise resolution to 'then' functions using $apply(). + * $rootScope.$apply(); + * expect(resolvedValue).toEqual(123); + * })); + * ``` + * + * @param {function(function, function)} resolver Function which is responsible for resolving or + * rejecting the newly created promise. The first parameter is a function which resolves the + * promise, the second parameter is a function which rejects the promise. + * + * @returns {Promise} The newly created promise. + */ +function $QProvider() { + + this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) { + return qFactory(function(callback) { + $rootScope.$evalAsync(callback); + }, $exceptionHandler); + }]; +} + +function $$QProvider() { + this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) { + return qFactory(function(callback) { + $browser.defer(callback); + }, $exceptionHandler); + }]; +} + +/** + * Constructs a promise manager. + * + * @param {function(function)} nextTick Function for executing functions in the next turn. + * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for + * debugging purposes. + * @returns {object} Promise manager. + */ +function qFactory(nextTick, exceptionHandler) { + var $qMinErr = minErr('$q', TypeError); + + /** + * @ngdoc method + * @name ng.$q#defer + * @kind function + * + * @description + * Creates a `Deferred` object which represents a task which will finish in the future. + * + * @returns {Deferred} Returns a new instance of deferred. + */ + var defer = function() { + var d = new Deferred(); + //Necessary to support unbound execution :/ + d.resolve = simpleBind(d, d.resolve); + d.reject = simpleBind(d, d.reject); + d.notify = simpleBind(d, d.notify); + return d; + }; + + function Promise() { + this.$$state = { status: 0 }; + } + + extend(Promise.prototype, { + then: function(onFulfilled, onRejected, progressBack) { + if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) { + return this; + } + var result = new Deferred(); + + this.$$state.pending = this.$$state.pending || []; + this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]); + if (this.$$state.status > 0) scheduleProcessQueue(this.$$state); + + return result.promise; + }, + + "catch": function(callback) { + return this.then(null, callback); + }, + + "finally": function(callback, progressBack) { + return this.then(function(value) { + return handleCallback(value, true, callback); + }, function(error) { + return handleCallback(error, false, callback); + }, progressBack); + } + }); + + //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native + function simpleBind(context, fn) { + return function(value) { + fn.call(context, value); + }; + } + + function processQueue(state) { + var fn, deferred, pending; + + pending = state.pending; + state.processScheduled = false; + state.pending = undefined; + for (var i = 0, ii = pending.length; i < ii; ++i) { + deferred = pending[i][0]; + fn = pending[i][state.status]; + try { + if (isFunction(fn)) { + deferred.resolve(fn(state.value)); + } else if (state.status === 1) { + deferred.resolve(state.value); + } else { + deferred.reject(state.value); + } + } catch (e) { + deferred.reject(e); + exceptionHandler(e); + } + } + } + + function scheduleProcessQueue(state) { + if (state.processScheduled || !state.pending) return; + state.processScheduled = true; + nextTick(function() { processQueue(state); }); + } + + function Deferred() { + this.promise = new Promise(); + } + + extend(Deferred.prototype, { + resolve: function(val) { + if (this.promise.$$state.status) return; + if (val === this.promise) { + this.$$reject($qMinErr( + 'qcycle', + "Expected promise to be resolved with value other than itself '{0}'", + val)); + } else { + this.$$resolve(val); + } + + }, + + $$resolve: function(val) { + var then; + var that = this; + var done = false; + try { + if ((isObject(val) || isFunction(val))) then = val && val.then; + if (isFunction(then)) { + this.promise.$$state.status = -1; + then.call(val, resolvePromise, rejectPromise, simpleBind(this, this.notify)); + } else { + this.promise.$$state.value = val; + this.promise.$$state.status = 1; + scheduleProcessQueue(this.promise.$$state); + } + } catch (e) { + rejectPromise(e); + exceptionHandler(e); + } + + function resolvePromise(val) { + if (done) return; + done = true; + that.$$resolve(val); + } + function rejectPromise(val) { + if (done) return; + done = true; + that.$$reject(val); + } + }, + + reject: function(reason) { + if (this.promise.$$state.status) return; + this.$$reject(reason); + }, + + $$reject: function(reason) { + this.promise.$$state.value = reason; + this.promise.$$state.status = 2; + scheduleProcessQueue(this.promise.$$state); + }, + + notify: function(progress) { + var callbacks = this.promise.$$state.pending; + + if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) { + nextTick(function() { + var callback, result; + for (var i = 0, ii = callbacks.length; i < ii; i++) { + result = callbacks[i][0]; + callback = callbacks[i][3]; + try { + result.notify(isFunction(callback) ? callback(progress) : progress); + } catch (e) { + exceptionHandler(e); + } + } + }); + } + } + }); + + /** + * @ngdoc method + * @name $q#reject + * @kind function + * + * @description + * Creates a promise that is resolved as rejected with the specified `reason`. This api should be + * used to forward rejection in a chain of promises. If you are dealing with the last promise in + * a promise chain, you don't need to worry about it. + * + * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of + * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via + * a promise error callback and you want to forward the error to the promise derived from the + * current promise, you have to "rethrow" the error by returning a rejection constructed via + * `reject`. + * + * ```js + * promiseB = promiseA.then(function(result) { + * // success: do something and resolve promiseB + * // with the old or a new result + * return result; + * }, function(reason) { + * // error: handle the error if possible and + * // resolve promiseB with newPromiseOrValue, + * // otherwise forward the rejection to promiseB + * if (canHandle(reason)) { + * // handle the error and recover + * return newPromiseOrValue; + * } + * return $q.reject(reason); + * }); + * ``` + * + * @param {*} reason Constant, message, exception or an object representing the rejection reason. + * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`. + */ + var reject = function(reason) { + var result = new Deferred(); + result.reject(reason); + return result.promise; + }; + + var makePromise = function makePromise(value, resolved) { + var result = new Deferred(); + if (resolved) { + result.resolve(value); + } else { + result.reject(value); + } + return result.promise; + }; + + var handleCallback = function handleCallback(value, isResolved, callback) { + var callbackOutput = null; + try { + if (isFunction(callback)) callbackOutput = callback(); + } catch (e) { + return makePromise(e, false); + } + if (isPromiseLike(callbackOutput)) { + return callbackOutput.then(function() { + return makePromise(value, isResolved); + }, function(error) { + return makePromise(error, false); + }); + } else { + return makePromise(value, isResolved); + } + }; + + /** + * @ngdoc method + * @name $q#when + * @kind function + * + * @description + * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. + * This is useful when you are dealing with an object that might or might not be a promise, or if + * the promise comes from a source that can't be trusted. + * + * @param {*} value Value or a promise + * @param {Function=} successCallback + * @param {Function=} errorCallback + * @param {Function=} progressCallback + * @returns {Promise} Returns a promise of the passed value or promise + */ + + + var when = function(value, callback, errback, progressBack) { + var result = new Deferred(); + result.resolve(value); + return result.promise.then(callback, errback, progressBack); + }; + + /** + * @ngdoc method + * @name $q#resolve + * @kind function + * + * @description + * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6. + * + * @param {*} value Value or a promise + * @param {Function=} successCallback + * @param {Function=} errorCallback + * @param {Function=} progressCallback + * @returns {Promise} Returns a promise of the passed value or promise + */ + var resolve = when; + + /** + * @ngdoc method + * @name $q#all + * @kind function + * + * @description + * Combines multiple promises into a single promise that is resolved when all of the input + * promises are resolved. + * + * @param {Array.|Object.} promises An array or hash of promises. + * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values, + * each value corresponding to the promise at the same index/key in the `promises` array/hash. + * If any of the promises is resolved with a rejection, this resulting promise will be rejected + * with the same rejection value. + */ + + function all(promises) { + var deferred = new Deferred(), + counter = 0, + results = isArray(promises) ? [] : {}; + + forEach(promises, function(promise, key) { + counter++; + when(promise).then(function(value) { + if (results.hasOwnProperty(key)) return; + results[key] = value; + if (!(--counter)) deferred.resolve(results); + }, function(reason) { + if (results.hasOwnProperty(key)) return; + deferred.reject(reason); + }); + }); + + if (counter === 0) { + deferred.resolve(results); + } + + return deferred.promise; + } + + var $Q = function Q(resolver) { + if (!isFunction(resolver)) { + throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver); + } + + var deferred = new Deferred(); + + function resolveFn(value) { + deferred.resolve(value); + } + + function rejectFn(reason) { + deferred.reject(reason); + } + + resolver(resolveFn, rejectFn); + + return deferred.promise; + }; + + // Let's make the instanceof operator work for promises, so that + // `new $q(fn) instanceof $q` would evaluate to true. + $Q.prototype = Promise.prototype; + + $Q.defer = defer; + $Q.reject = reject; + $Q.when = when; + $Q.resolve = resolve; + $Q.all = all; + + return $Q; +} + +function $$RAFProvider() { //rAF + this.$get = ['$window', '$timeout', function($window, $timeout) { + var requestAnimationFrame = $window.requestAnimationFrame || + $window.webkitRequestAnimationFrame; + + var cancelAnimationFrame = $window.cancelAnimationFrame || + $window.webkitCancelAnimationFrame || + $window.webkitCancelRequestAnimationFrame; + + var rafSupported = !!requestAnimationFrame; + var raf = rafSupported + ? function(fn) { + var id = requestAnimationFrame(fn); + return function() { + cancelAnimationFrame(id); + }; + } + : function(fn) { + var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666 + return function() { + $timeout.cancel(timer); + }; + }; + + raf.supported = rafSupported; + + return raf; + }]; +} + +/** + * DESIGN NOTES + * + * The design decisions behind the scope are heavily favored for speed and memory consumption. + * + * The typical use of scope is to watch the expressions, which most of the time return the same + * value as last time so we optimize the operation. + * + * Closures construction is expensive in terms of speed as well as memory: + * - No closures, instead use prototypical inheritance for API + * - Internal state needs to be stored on scope directly, which means that private state is + * exposed as $$____ properties + * + * Loop operations are optimized by using while(count--) { ... } + * - This means that in order to keep the same order of execution as addition we have to add + * items to the array at the beginning (unshift) instead of at the end (push) + * + * Child scopes are created and removed often + * - Using an array would be slow since inserts in the middle are expensive; so we use linked lists + * + * There are fewer watches than observers. This is why you don't want the observer to be implemented + * in the same way as watch. Watch requires return of the initialization function which is expensive + * to construct. + */ + + +/** + * @ngdoc provider + * @name $rootScopeProvider + * @description + * + * Provider for the $rootScope service. + */ + +/** + * @ngdoc method + * @name $rootScopeProvider#digestTtl + * @description + * + * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and + * assuming that the model is unstable. + * + * The current default is 10 iterations. + * + * In complex applications it's possible that the dependencies between `$watch`s will result in + * several digest iterations. However if an application needs more than the default 10 digest + * iterations for its model to stabilize then you should investigate what is causing the model to + * continuously change during the digest. + * + * Increasing the TTL could have performance implications, so you should not change it without + * proper justification. + * + * @param {number} limit The number of digest iterations. + */ + + +/** + * @ngdoc service + * @name $rootScope + * @description + * + * Every application has a single root {@link ng.$rootScope.Scope scope}. + * All other scopes are descendant scopes of the root scope. Scopes provide separation + * between the model and the view, via a mechanism for watching the model for changes. + * They also provide event emission/broadcast and subscription facility. See the + * {@link guide/scope developer guide on scopes}. + */ +function $RootScopeProvider() { + var TTL = 10; + var $rootScopeMinErr = minErr('$rootScope'); + var lastDirtyWatch = null; + var applyAsyncId = null; + + this.digestTtl = function(value) { + if (arguments.length) { + TTL = value; + } + return TTL; + }; + + function createChildScopeClass(parent) { + function ChildScope() { + this.$$watchers = this.$$nextSibling = + this.$$childHead = this.$$childTail = null; + this.$$listeners = {}; + this.$$listenerCount = {}; + this.$$watchersCount = 0; + this.$id = nextUid(); + this.$$ChildScope = null; + } + ChildScope.prototype = parent; + return ChildScope; + } + + this.$get = ['$exceptionHandler', '$parse', '$browser', + function($exceptionHandler, $parse, $browser) { + + function destroyChildScope($event) { + $event.currentScope.$$destroyed = true; + } + + function cleanUpScope($scope) { + + if (msie === 9) { + // There is a memory leak in IE9 if all child scopes are not disconnected + // completely when a scope is destroyed. So this code will recurse up through + // all this scopes children + // + // See issue https://github.com/angular/angular.js/issues/10706 + $scope.$$childHead && cleanUpScope($scope.$$childHead); + $scope.$$nextSibling && cleanUpScope($scope.$$nextSibling); + } + + // The code below works around IE9 and V8's memory leaks + // + // See: + // - https://code.google.com/p/v8/issues/detail?id=2073#c26 + // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909 + // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451 + + $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead = + $scope.$$childTail = $scope.$root = $scope.$$watchers = null; + } + + /** + * @ngdoc type + * @name $rootScope.Scope + * + * @description + * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the + * {@link auto.$injector $injector}. Child scopes are created using the + * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when + * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for + * an in-depth introduction and usage examples. + * + * + * # Inheritance + * A scope can inherit from a parent scope, as in this example: + * ```js + var parent = $rootScope; + var child = parent.$new(); + + parent.salutation = "Hello"; + expect(child.salutation).toEqual('Hello'); + + child.salutation = "Welcome"; + expect(child.salutation).toEqual('Welcome'); + expect(parent.salutation).toEqual('Hello'); + * ``` + * + * When interacting with `Scope` in tests, additional helper methods are available on the + * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional + * details. + * + * + * @param {Object.=} providers Map of service factory which need to be + * provided for the current scope. Defaults to {@link ng}. + * @param {Object.=} instanceCache Provides pre-instantiated services which should + * append/override services provided by `providers`. This is handy + * when unit-testing and having the need to override a default + * service. + * @returns {Object} Newly created scope. + * + */ + function Scope() { + this.$id = nextUid(); + this.$$phase = this.$parent = this.$$watchers = + this.$$nextSibling = this.$$prevSibling = + this.$$childHead = this.$$childTail = null; + this.$root = this; + this.$$destroyed = false; + this.$$listeners = {}; + this.$$listenerCount = {}; + this.$$watchersCount = 0; + this.$$isolateBindings = null; + } + + /** + * @ngdoc property + * @name $rootScope.Scope#$id + * + * @description + * Unique scope ID (monotonically increasing) useful for debugging. + */ + + /** + * @ngdoc property + * @name $rootScope.Scope#$parent + * + * @description + * Reference to the parent scope. + */ + + /** + * @ngdoc property + * @name $rootScope.Scope#$root + * + * @description + * Reference to the root scope. + */ + + Scope.prototype = { + constructor: Scope, + /** + * @ngdoc method + * @name $rootScope.Scope#$new + * @kind function + * + * @description + * Creates a new child {@link ng.$rootScope.Scope scope}. + * + * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event. + * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}. + * + * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is + * desired for the scope and its child scopes to be permanently detached from the parent and + * thus stop participating in model change detection and listener notification by invoking. + * + * @param {boolean} isolate If true, then the scope does not prototypically inherit from the + * parent scope. The scope is isolated, as it can not see parent scope properties. + * When creating widgets, it is useful for the widget to not accidentally read parent + * state. + * + * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent` + * of the newly created scope. Defaults to `this` scope if not provided. + * This is used when creating a transclude scope to correctly place it + * in the scope hierarchy while maintaining the correct prototypical + * inheritance. + * + * @returns {Object} The newly created child scope. + * + */ + $new: function(isolate, parent) { + var child; + + parent = parent || this; + + if (isolate) { + child = new Scope(); + child.$root = this.$root; + } else { + // Only create a child scope class if somebody asks for one, + // but cache it to allow the VM to optimize lookups. + if (!this.$$ChildScope) { + this.$$ChildScope = createChildScopeClass(this); + } + child = new this.$$ChildScope(); + } + child.$parent = parent; + child.$$prevSibling = parent.$$childTail; + if (parent.$$childHead) { + parent.$$childTail.$$nextSibling = child; + parent.$$childTail = child; + } else { + parent.$$childHead = parent.$$childTail = child; + } + + // When the new scope is not isolated or we inherit from `this`, and + // the parent scope is destroyed, the property `$$destroyed` is inherited + // prototypically. In all other cases, this property needs to be set + // when the parent scope is destroyed. + // The listener needs to be added after the parent is set + if (isolate || parent != this) child.$on('$destroy', destroyChildScope); + + return child; + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$watch + * @kind function + * + * @description + * Registers a `listener` callback to be executed whenever the `watchExpression` changes. + * + * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest + * $digest()} and should return the value that will be watched. (`watchExpression` should not change + * its value when executed multiple times with the same input because it may be executed multiple + * times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be + * [idempotent](http://en.wikipedia.org/wiki/Idempotence). + * - The `listener` is called only when the value from the current `watchExpression` and the + * previous call to `watchExpression` are not equal (with the exception of the initial run, + * see below). Inequality is determined according to reference inequality, + * [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators) + * via the `!==` Javascript operator, unless `objectEquality == true` + * (see next point) + * - When `objectEquality == true`, inequality of the `watchExpression` is determined + * according to the {@link angular.equals} function. To save the value of the object for + * later comparison, the {@link angular.copy} function is used. This therefore means that + * watching complex objects will have adverse memory and performance implications. + * - The watch `listener` may change the model, which may trigger other `listener`s to fire. + * This is achieved by rerunning the watchers until no changes are detected. The rerun + * iteration limit is 10 to prevent an infinite loop deadlock. + * + * + * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called, + * you can register a `watchExpression` function with no `listener`. (Be prepared for + * multiple calls to your `watchExpression` because it will execute multiple times in a + * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.) + * + * After a watcher is registered with the scope, the `listener` fn is called asynchronously + * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the + * watcher. In rare cases, this is undesirable because the listener is called when the result + * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you + * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the + * listener was called due to initialization. + * + * + * + * # Example + * ```js + // let's assume that scope was dependency injected as the $rootScope + var scope = $rootScope; + scope.name = 'misko'; + scope.counter = 0; + + expect(scope.counter).toEqual(0); + scope.$watch('name', function(newValue, oldValue) { + scope.counter = scope.counter + 1; + }); + expect(scope.counter).toEqual(0); + + scope.$digest(); + // the listener is always called during the first $digest loop after it was registered + expect(scope.counter).toEqual(1); + + scope.$digest(); + // but now it will not be called unless the value changes + expect(scope.counter).toEqual(1); + + scope.name = 'adam'; + scope.$digest(); + expect(scope.counter).toEqual(2); + + + + // Using a function as a watchExpression + var food; + scope.foodCounter = 0; + expect(scope.foodCounter).toEqual(0); + scope.$watch( + // This function returns the value being watched. It is called for each turn of the $digest loop + function() { return food; }, + // This is the change listener, called when the value returned from the above function changes + function(newValue, oldValue) { + if ( newValue !== oldValue ) { + // Only increment the counter if the value changed + scope.foodCounter = scope.foodCounter + 1; + } + } + ); + // No digest has been run so the counter will be zero + expect(scope.foodCounter).toEqual(0); + + // Run the digest but since food has not changed count will still be zero + scope.$digest(); + expect(scope.foodCounter).toEqual(0); + + // Update food and run digest. Now the counter will increment + food = 'cheeseburger'; + scope.$digest(); + expect(scope.foodCounter).toEqual(1); + + * ``` + * + * + * + * @param {(function()|string)} watchExpression Expression that is evaluated on each + * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers + * a call to the `listener`. + * + * - `string`: Evaluated as {@link guide/expression expression} + * - `function(scope)`: called with current `scope` as a parameter. + * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value + * of `watchExpression` changes. + * + * - `newVal` contains the current value of the `watchExpression` + * - `oldVal` contains the previous value of the `watchExpression` + * - `scope` refers to the current scope + * @param {boolean=} [objectEquality=false] Compare for object equality using {@link angular.equals} instead of + * comparing for reference equality. + * @returns {function()} Returns a deregistration function for this listener. + */ + $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) { + var get = $parse(watchExp); + + if (get.$$watchDelegate) { + return get.$$watchDelegate(this, listener, objectEquality, get, watchExp); + } + var scope = this, + array = scope.$$watchers, + watcher = { + fn: listener, + last: initWatchVal, + get: get, + exp: prettyPrintExpression || watchExp, + eq: !!objectEquality + }; + + lastDirtyWatch = null; + + if (!isFunction(listener)) { + watcher.fn = noop; + } + + if (!array) { + array = scope.$$watchers = []; + } + // we use unshift since we use a while loop in $digest for speed. + // the while loop reads in reverse order. + array.unshift(watcher); + incrementWatchersCount(this, 1); + + return function deregisterWatch() { + if (arrayRemove(array, watcher) >= 0) { + incrementWatchersCount(scope, -1); + } + lastDirtyWatch = null; + }; + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$watchGroup + * @kind function + * + * @description + * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`. + * If any one expression in the collection changes the `listener` is executed. + * + * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every + * call to $digest() to see if any items changes. + * - The `listener` is called whenever any expression in the `watchExpressions` array changes. + * + * @param {Array.} watchExpressions Array of expressions that will be individually + * watched using {@link ng.$rootScope.Scope#$watch $watch()} + * + * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any + * expression in `watchExpressions` changes + * The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching + * those of `watchExpression` + * and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching + * those of `watchExpression` + * The `scope` refers to the current scope. + * @returns {function()} Returns a de-registration function for all listeners. + */ + $watchGroup: function(watchExpressions, listener) { + var oldValues = new Array(watchExpressions.length); + var newValues = new Array(watchExpressions.length); + var deregisterFns = []; + var self = this; + var changeReactionScheduled = false; + var firstRun = true; + + if (!watchExpressions.length) { + // No expressions means we call the listener ASAP + var shouldCall = true; + self.$evalAsync(function() { + if (shouldCall) listener(newValues, newValues, self); + }); + return function deregisterWatchGroup() { + shouldCall = false; + }; + } + + if (watchExpressions.length === 1) { + // Special case size of one + return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) { + newValues[0] = value; + oldValues[0] = oldValue; + listener(newValues, (value === oldValue) ? newValues : oldValues, scope); + }); + } + + forEach(watchExpressions, function(expr, i) { + var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) { + newValues[i] = value; + oldValues[i] = oldValue; + if (!changeReactionScheduled) { + changeReactionScheduled = true; + self.$evalAsync(watchGroupAction); + } + }); + deregisterFns.push(unwatchFn); + }); + + function watchGroupAction() { + changeReactionScheduled = false; + + if (firstRun) { + firstRun = false; + listener(newValues, newValues, self); + } else { + listener(newValues, oldValues, self); + } + } + + return function deregisterWatchGroup() { + while (deregisterFns.length) { + deregisterFns.shift()(); + } + }; + }, + + + /** + * @ngdoc method + * @name $rootScope.Scope#$watchCollection + * @kind function + * + * @description + * Shallow watches the properties of an object and fires whenever any of the properties change + * (for arrays, this implies watching the array items; for object maps, this implies watching + * the properties). If a change is detected, the `listener` callback is fired. + * + * - The `obj` collection is observed via standard $watch operation and is examined on every + * call to $digest() to see if any items have been added, removed, or moved. + * - The `listener` is called whenever anything within the `obj` has changed. Examples include + * adding, removing, and moving items belonging to an object or array. + * + * + * # Example + * ```js + $scope.names = ['igor', 'matias', 'misko', 'james']; + $scope.dataCount = 4; + + $scope.$watchCollection('names', function(newNames, oldNames) { + $scope.dataCount = newNames.length; + }); + + expect($scope.dataCount).toEqual(4); + $scope.$digest(); + + //still at 4 ... no changes + expect($scope.dataCount).toEqual(4); + + $scope.names.pop(); + $scope.$digest(); + + //now there's been a change + expect($scope.dataCount).toEqual(3); + * ``` + * + * + * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The + * expression value should evaluate to an object or an array which is observed on each + * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the + * collection will trigger a call to the `listener`. + * + * @param {function(newCollection, oldCollection, scope)} listener a callback function called + * when a change is detected. + * - The `newCollection` object is the newly modified data obtained from the `obj` expression + * - The `oldCollection` object is a copy of the former collection data. + * Due to performance considerations, the`oldCollection` value is computed only if the + * `listener` function declares two or more arguments. + * - The `scope` argument refers to the current scope. + * + * @returns {function()} Returns a de-registration function for this listener. When the + * de-registration function is executed, the internal watch operation is terminated. + */ + $watchCollection: function(obj, listener) { + $watchCollectionInterceptor.$stateful = true; + + var self = this; + // the current value, updated on each dirty-check run + var newValue; + // a shallow copy of the newValue from the last dirty-check run, + // updated to match newValue during dirty-check run + var oldValue; + // a shallow copy of the newValue from when the last change happened + var veryOldValue; + // only track veryOldValue if the listener is asking for it + var trackVeryOldValue = (listener.length > 1); + var changeDetected = 0; + var changeDetector = $parse(obj, $watchCollectionInterceptor); + var internalArray = []; + var internalObject = {}; + var initRun = true; + var oldLength = 0; + + function $watchCollectionInterceptor(_value) { + newValue = _value; + var newLength, key, bothNaN, newItem, oldItem; + + // If the new value is undefined, then return undefined as the watch may be a one-time watch + if (isUndefined(newValue)) return; + + if (!isObject(newValue)) { // if primitive + if (oldValue !== newValue) { + oldValue = newValue; + changeDetected++; + } + } else if (isArrayLike(newValue)) { + if (oldValue !== internalArray) { + // we are transitioning from something which was not an array into array. + oldValue = internalArray; + oldLength = oldValue.length = 0; + changeDetected++; + } + + newLength = newValue.length; + + if (oldLength !== newLength) { + // if lengths do not match we need to trigger change notification + changeDetected++; + oldValue.length = oldLength = newLength; + } + // copy the items to oldValue and look for changes. + for (var i = 0; i < newLength; i++) { + oldItem = oldValue[i]; + newItem = newValue[i]; + + bothNaN = (oldItem !== oldItem) && (newItem !== newItem); + if (!bothNaN && (oldItem !== newItem)) { + changeDetected++; + oldValue[i] = newItem; + } + } + } else { + if (oldValue !== internalObject) { + // we are transitioning from something which was not an object into object. + oldValue = internalObject = {}; + oldLength = 0; + changeDetected++; + } + // copy the items to oldValue and look for changes. + newLength = 0; + for (key in newValue) { + if (hasOwnProperty.call(newValue, key)) { + newLength++; + newItem = newValue[key]; + oldItem = oldValue[key]; + + if (key in oldValue) { + bothNaN = (oldItem !== oldItem) && (newItem !== newItem); + if (!bothNaN && (oldItem !== newItem)) { + changeDetected++; + oldValue[key] = newItem; + } + } else { + oldLength++; + oldValue[key] = newItem; + changeDetected++; + } + } + } + if (oldLength > newLength) { + // we used to have more keys, need to find them and destroy them. + changeDetected++; + for (key in oldValue) { + if (!hasOwnProperty.call(newValue, key)) { + oldLength--; + delete oldValue[key]; + } + } + } + } + return changeDetected; + } + + function $watchCollectionAction() { + if (initRun) { + initRun = false; + listener(newValue, newValue, self); + } else { + listener(newValue, veryOldValue, self); + } + + // make a copy for the next time a collection is changed + if (trackVeryOldValue) { + if (!isObject(newValue)) { + //primitive + veryOldValue = newValue; + } else if (isArrayLike(newValue)) { + veryOldValue = new Array(newValue.length); + for (var i = 0; i < newValue.length; i++) { + veryOldValue[i] = newValue[i]; + } + } else { // if object + veryOldValue = {}; + for (var key in newValue) { + if (hasOwnProperty.call(newValue, key)) { + veryOldValue[key] = newValue[key]; + } + } + } + } + } + + return this.$watch(changeDetector, $watchCollectionAction); + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$digest + * @kind function + * + * @description + * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and + * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change + * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} + * until no more listeners are firing. This means that it is possible to get into an infinite + * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of + * iterations exceeds 10. + * + * Usually, you don't call `$digest()` directly in + * {@link ng.directive:ngController controllers} or in + * {@link ng.$compileProvider#directive directives}. + * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within + * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`. + * + * If you want to be notified whenever `$digest()` is called, + * you can register a `watchExpression` function with + * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`. + * + * In unit tests, you may need to call `$digest()` to simulate the scope life cycle. + * + * # Example + * ```js + var scope = ...; + scope.name = 'misko'; + scope.counter = 0; + + expect(scope.counter).toEqual(0); + scope.$watch('name', function(newValue, oldValue) { + scope.counter = scope.counter + 1; + }); + expect(scope.counter).toEqual(0); + + scope.$digest(); + // the listener is always called during the first $digest loop after it was registered + expect(scope.counter).toEqual(1); + + scope.$digest(); + // but now it will not be called unless the value changes + expect(scope.counter).toEqual(1); + + scope.name = 'adam'; + scope.$digest(); + expect(scope.counter).toEqual(2); + * ``` + * + */ + $digest: function() { + var watch, value, last, fn, get, + watchers, + length, + dirty, ttl = TTL, + next, current, target = this, + watchLog = [], + logIdx, asyncTask; + + beginPhase('$digest'); + // Check for changes to browser url that happened in sync before the call to $digest + $browser.$$checkUrlChange(); + + if (this === $rootScope && applyAsyncId !== null) { + // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then + // cancel the scheduled $apply and flush the queue of expressions to be evaluated. + $browser.defer.cancel(applyAsyncId); + flushApplyAsync(); + } + + lastDirtyWatch = null; + + do { // "while dirty" loop + dirty = false; + current = target; + + while (asyncQueue.length) { + try { + asyncTask = asyncQueue.shift(); + asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals); + } catch (e) { + $exceptionHandler(e); + } + lastDirtyWatch = null; + } + + traverseScopesLoop: + do { // "traverse the scopes" loop + if ((watchers = current.$$watchers)) { + // process our watches + length = watchers.length; + while (length--) { + try { + watch = watchers[length]; + // Most common watches are on primitives, in which case we can short + // circuit it with === operator, only when === fails do we use .equals + if (watch) { + get = watch.get; + if ((value = get(current)) !== (last = watch.last) && + !(watch.eq + ? equals(value, last) + : (typeof value === 'number' && typeof last === 'number' + && isNaN(value) && isNaN(last)))) { + dirty = true; + lastDirtyWatch = watch; + watch.last = watch.eq ? copy(value, null) : value; + fn = watch.fn; + fn(value, ((last === initWatchVal) ? value : last), current); + if (ttl < 5) { + logIdx = 4 - ttl; + if (!watchLog[logIdx]) watchLog[logIdx] = []; + watchLog[logIdx].push({ + msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp, + newVal: value, + oldVal: last + }); + } + } else if (watch === lastDirtyWatch) { + // If the most recently dirty watcher is now clean, short circuit since the remaining watchers + // have already been tested. + dirty = false; + break traverseScopesLoop; + } + } + } catch (e) { + $exceptionHandler(e); + } + } + } + + // Insanity Warning: scope depth-first traversal + // yes, this code is a bit crazy, but it works and we have tests to prove it! + // this piece should be kept in sync with the traversal in $broadcast + if (!(next = ((current.$$watchersCount && current.$$childHead) || + (current !== target && current.$$nextSibling)))) { + while (current !== target && !(next = current.$$nextSibling)) { + current = current.$parent; + } + } + } while ((current = next)); + + // `break traverseScopesLoop;` takes us to here + + if ((dirty || asyncQueue.length) && !(ttl--)) { + clearPhase(); + throw $rootScopeMinErr('infdig', + '{0} $digest() iterations reached. Aborting!\n' + + 'Watchers fired in the last 5 iterations: {1}', + TTL, watchLog); + } + + } while (dirty || asyncQueue.length); + + clearPhase(); + + while (postDigestQueue.length) { + try { + postDigestQueue.shift()(); + } catch (e) { + $exceptionHandler(e); + } + } + }, + + + /** + * @ngdoc event + * @name $rootScope.Scope#$destroy + * @eventType broadcast on scope being destroyed + * + * @description + * Broadcasted when a scope and its children are being destroyed. + * + * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to + * clean up DOM bindings before an element is removed from the DOM. + */ + + /** + * @ngdoc method + * @name $rootScope.Scope#$destroy + * @kind function + * + * @description + * Removes the current scope (and all of its children) from the parent scope. Removal implies + * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer + * propagate to the current scope and its children. Removal also implies that the current + * scope is eligible for garbage collection. + * + * The `$destroy()` is usually used by directives such as + * {@link ng.directive:ngRepeat ngRepeat} for managing the + * unrolling of the loop. + * + * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope. + * Application code can register a `$destroy` event handler that will give it a chance to + * perform any necessary cleanup. + * + * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to + * clean up DOM bindings before an element is removed from the DOM. + */ + $destroy: function() { + // We can't destroy a scope that has been already destroyed. + if (this.$$destroyed) return; + var parent = this.$parent; + + this.$broadcast('$destroy'); + this.$$destroyed = true; + + if (this === $rootScope) { + //Remove handlers attached to window when $rootScope is removed + $browser.$$applicationDestroyed(); + } + + incrementWatchersCount(this, -this.$$watchersCount); + for (var eventName in this.$$listenerCount) { + decrementListenerCount(this, this.$$listenerCount[eventName], eventName); + } + + // sever all the references to parent scopes (after this cleanup, the current scope should + // not be retained by any of our references and should be eligible for garbage collection) + if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling; + if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling; + if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling; + if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling; + + // Disable listeners, watchers and apply/digest methods + this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop; + this.$on = this.$watch = this.$watchGroup = function() { return noop; }; + this.$$listeners = {}; + + // Disconnect the next sibling to prevent `cleanUpScope` destroying those too + this.$$nextSibling = null; + cleanUpScope(this); + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$eval + * @kind function + * + * @description + * Executes the `expression` on the current scope and returns the result. Any exceptions in + * the expression are propagated (uncaught). This is useful when evaluating Angular + * expressions. + * + * # Example + * ```js + var scope = ng.$rootScope.Scope(); + scope.a = 1; + scope.b = 2; + + expect(scope.$eval('a+b')).toEqual(3); + expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3); + * ``` + * + * @param {(string|function())=} expression An angular expression to be executed. + * + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with the current `scope` parameter. + * + * @param {(object)=} locals Local variables object, useful for overriding values in scope. + * @returns {*} The result of evaluating the expression. + */ + $eval: function(expr, locals) { + return $parse(expr)(this, locals); + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$evalAsync + * @kind function + * + * @description + * Executes the expression on the current scope at a later point in time. + * + * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only + * that: + * + * - it will execute after the function that scheduled the evaluation (preferably before DOM + * rendering). + * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after + * `expression` execution. + * + * Any exceptions from the execution of the expression are forwarded to the + * {@link ng.$exceptionHandler $exceptionHandler} service. + * + * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle + * will be scheduled. However, it is encouraged to always call code that changes the model + * from within an `$apply` call. That includes code evaluated via `$evalAsync`. + * + * @param {(string|function())=} expression An angular expression to be executed. + * + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with the current `scope` parameter. + * + * @param {(object)=} locals Local variables object, useful for overriding values in scope. + */ + $evalAsync: function(expr, locals) { + // if we are outside of an $digest loop and this is the first time we are scheduling async + // task also schedule async auto-flush + if (!$rootScope.$$phase && !asyncQueue.length) { + $browser.defer(function() { + if (asyncQueue.length) { + $rootScope.$digest(); + } + }); + } + + asyncQueue.push({scope: this, expression: $parse(expr), locals: locals}); + }, + + $$postDigest: function(fn) { + postDigestQueue.push(fn); + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$apply + * @kind function + * + * @description + * `$apply()` is used to execute an expression in angular from outside of the angular + * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). + * Because we are calling into the angular framework we need to perform proper scope life + * cycle of {@link ng.$exceptionHandler exception handling}, + * {@link ng.$rootScope.Scope#$digest executing watches}. + * + * ## Life cycle + * + * # Pseudo-Code of `$apply()` + * ```js + function $apply(expr) { + try { + return $eval(expr); + } catch (e) { + $exceptionHandler(e); + } finally { + $root.$digest(); + } + } + * ``` + * + * + * Scope's `$apply()` method transitions through the following stages: + * + * 1. The {@link guide/expression expression} is executed using the + * {@link ng.$rootScope.Scope#$eval $eval()} method. + * 2. Any exceptions from the execution of the expression are forwarded to the + * {@link ng.$exceptionHandler $exceptionHandler} service. + * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the + * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method. + * + * + * @param {(string|function())=} exp An angular expression to be executed. + * + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with current `scope` parameter. + * + * @returns {*} The result of evaluating the expression. + */ + $apply: function(expr) { + try { + beginPhase('$apply'); + try { + return this.$eval(expr); + } finally { + clearPhase(); + } + } catch (e) { + $exceptionHandler(e); + } finally { + try { + $rootScope.$digest(); + } catch (e) { + $exceptionHandler(e); + throw e; + } + } + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$applyAsync + * @kind function + * + * @description + * Schedule the invocation of $apply to occur at a later time. The actual time difference + * varies across browsers, but is typically around ~10 milliseconds. + * + * This can be used to queue up multiple expressions which need to be evaluated in the same + * digest. + * + * @param {(string|function())=} exp An angular expression to be executed. + * + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with current `scope` parameter. + */ + $applyAsync: function(expr) { + var scope = this; + expr && applyAsyncQueue.push($applyAsyncExpression); + expr = $parse(expr); + scheduleApplyAsync(); + + function $applyAsyncExpression() { + scope.$eval(expr); + } + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$on + * @kind function + * + * @description + * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for + * discussion of event life cycle. + * + * The event listener function format is: `function(event, args...)`. The `event` object + * passed into the listener has the following attributes: + * + * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or + * `$broadcast`-ed. + * - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the + * event propagates through the scope hierarchy, this property is set to null. + * - `name` - `{string}`: name of the event. + * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel + * further event propagation (available only for events that were `$emit`-ed). + * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag + * to true. + * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called. + * + * @param {string} name Event name to listen on. + * @param {function(event, ...args)} listener Function to call when the event is emitted. + * @returns {function()} Returns a deregistration function for this listener. + */ + $on: function(name, listener) { + var namedListeners = this.$$listeners[name]; + if (!namedListeners) { + this.$$listeners[name] = namedListeners = []; + } + namedListeners.push(listener); + + var current = this; + do { + if (!current.$$listenerCount[name]) { + current.$$listenerCount[name] = 0; + } + current.$$listenerCount[name]++; + } while ((current = current.$parent)); + + var self = this; + return function() { + var indexOfListener = namedListeners.indexOf(listener); + if (indexOfListener !== -1) { + namedListeners[indexOfListener] = null; + decrementListenerCount(self, 1, name); + } + }; + }, + + + /** + * @ngdoc method + * @name $rootScope.Scope#$emit + * @kind function + * + * @description + * Dispatches an event `name` upwards through the scope hierarchy notifying the + * registered {@link ng.$rootScope.Scope#$on} listeners. + * + * The event life cycle starts at the scope on which `$emit` was called. All + * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get + * notified. Afterwards, the event traverses upwards toward the root scope and calls all + * registered listeners along the way. The event will stop propagating if one of the listeners + * cancels it. + * + * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed + * onto the {@link ng.$exceptionHandler $exceptionHandler} service. + * + * @param {string} name Event name to emit. + * @param {...*} args Optional one or more arguments which will be passed onto the event listeners. + * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}). + */ + $emit: function(name, args) { + var empty = [], + namedListeners, + scope = this, + stopPropagation = false, + event = { + name: name, + targetScope: scope, + stopPropagation: function() {stopPropagation = true;}, + preventDefault: function() { + event.defaultPrevented = true; + }, + defaultPrevented: false + }, + listenerArgs = concat([event], arguments, 1), + i, length; + + do { + namedListeners = scope.$$listeners[name] || empty; + event.currentScope = scope; + for (i = 0, length = namedListeners.length; i < length; i++) { + + // if listeners were deregistered, defragment the array + if (!namedListeners[i]) { + namedListeners.splice(i, 1); + i--; + length--; + continue; + } + try { + //allow all listeners attached to the current scope to run + namedListeners[i].apply(null, listenerArgs); + } catch (e) { + $exceptionHandler(e); + } + } + //if any listener on the current scope stops propagation, prevent bubbling + if (stopPropagation) { + event.currentScope = null; + return event; + } + //traverse upwards + scope = scope.$parent; + } while (scope); + + event.currentScope = null; + + return event; + }, + + + /** + * @ngdoc method + * @name $rootScope.Scope#$broadcast + * @kind function + * + * @description + * Dispatches an event `name` downwards to all child scopes (and their children) notifying the + * registered {@link ng.$rootScope.Scope#$on} listeners. + * + * The event life cycle starts at the scope on which `$broadcast` was called. All + * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get + * notified. Afterwards, the event propagates to all direct and indirect scopes of the current + * scope and calls all registered listeners along the way. The event cannot be canceled. + * + * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed + * onto the {@link ng.$exceptionHandler $exceptionHandler} service. + * + * @param {string} name Event name to broadcast. + * @param {...*} args Optional one or more arguments which will be passed onto the event listeners. + * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on} + */ + $broadcast: function(name, args) { + var target = this, + current = target, + next = target, + event = { + name: name, + targetScope: target, + preventDefault: function() { + event.defaultPrevented = true; + }, + defaultPrevented: false + }; + + if (!target.$$listenerCount[name]) return event; + + var listenerArgs = concat([event], arguments, 1), + listeners, i, length; + + //down while you can, then up and next sibling or up and next sibling until back at root + while ((current = next)) { + event.currentScope = current; + listeners = current.$$listeners[name] || []; + for (i = 0, length = listeners.length; i < length; i++) { + // if listeners were deregistered, defragment the array + if (!listeners[i]) { + listeners.splice(i, 1); + i--; + length--; + continue; + } + + try { + listeners[i].apply(null, listenerArgs); + } catch (e) { + $exceptionHandler(e); + } + } + + // Insanity Warning: scope depth-first traversal + // yes, this code is a bit crazy, but it works and we have tests to prove it! + // this piece should be kept in sync with the traversal in $digest + // (though it differs due to having the extra check for $$listenerCount) + if (!(next = ((current.$$listenerCount[name] && current.$$childHead) || + (current !== target && current.$$nextSibling)))) { + while (current !== target && !(next = current.$$nextSibling)) { + current = current.$parent; + } + } + } + + event.currentScope = null; + return event; + } + }; + + var $rootScope = new Scope(); + + //The internal queues. Expose them on the $rootScope for debugging/testing purposes. + var asyncQueue = $rootScope.$$asyncQueue = []; + var postDigestQueue = $rootScope.$$postDigestQueue = []; + var applyAsyncQueue = $rootScope.$$applyAsyncQueue = []; + + return $rootScope; + + + function beginPhase(phase) { + if ($rootScope.$$phase) { + throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase); + } + + $rootScope.$$phase = phase; + } + + function clearPhase() { + $rootScope.$$phase = null; + } + + function incrementWatchersCount(current, count) { + do { + current.$$watchersCount += count; + } while ((current = current.$parent)); + } + + function decrementListenerCount(current, count, name) { + do { + current.$$listenerCount[name] -= count; + + if (current.$$listenerCount[name] === 0) { + delete current.$$listenerCount[name]; + } + } while ((current = current.$parent)); + } + + /** + * function used as an initial value for watchers. + * because it's unique we can easily tell it apart from other values + */ + function initWatchVal() {} + + function flushApplyAsync() { + while (applyAsyncQueue.length) { + try { + applyAsyncQueue.shift()(); + } catch (e) { + $exceptionHandler(e); + } + } + applyAsyncId = null; + } + + function scheduleApplyAsync() { + if (applyAsyncId === null) { + applyAsyncId = $browser.defer(function() { + $rootScope.$apply(flushApplyAsync); + }); + } + } + }]; +} + +/** + * @ngdoc service + * @name $rootElement + * + * @description + * The root element of Angular application. This is either the element where {@link + * ng.directive:ngApp ngApp} was declared or the element passed into + * {@link angular.bootstrap}. The element represents the root element of application. It is also the + * location where the application's {@link auto.$injector $injector} service gets + * published, and can be retrieved using `$rootElement.injector()`. + */ + + +// the implementation is in angular.bootstrap + +/** + * @description + * Private service to sanitize uris for links and images. Used by $compile and $sanitize. + */ +function $$SanitizeUriProvider() { + var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/, + imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/; + + /** + * @description + * Retrieves or overrides the default regular expression that is used for whitelisting of safe + * urls during a[href] sanitization. + * + * The sanitization is a security measure aimed at prevent XSS attacks via html links. + * + * Any url about to be assigned to a[href] via data-binding is first normalized and turned into + * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist` + * regular expression. If a match is found, the original url is written into the dom. Otherwise, + * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. + * + * @param {RegExp=} regexp New regexp to whitelist urls with. + * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for + * chaining otherwise. + */ + this.aHrefSanitizationWhitelist = function(regexp) { + if (isDefined(regexp)) { + aHrefSanitizationWhitelist = regexp; + return this; + } + return aHrefSanitizationWhitelist; + }; + + + /** + * @description + * Retrieves or overrides the default regular expression that is used for whitelisting of safe + * urls during img[src] sanitization. + * + * The sanitization is a security measure aimed at prevent XSS attacks via html links. + * + * Any url about to be assigned to img[src] via data-binding is first normalized and turned into + * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist` + * regular expression. If a match is found, the original url is written into the dom. Otherwise, + * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. + * + * @param {RegExp=} regexp New regexp to whitelist urls with. + * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for + * chaining otherwise. + */ + this.imgSrcSanitizationWhitelist = function(regexp) { + if (isDefined(regexp)) { + imgSrcSanitizationWhitelist = regexp; + return this; + } + return imgSrcSanitizationWhitelist; + }; + + this.$get = function() { + return function sanitizeUri(uri, isImage) { + var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist; + var normalizedVal; + normalizedVal = urlResolve(uri).href; + if (normalizedVal !== '' && !normalizedVal.match(regex)) { + return 'unsafe:' + normalizedVal; + } + return uri; + }; + }; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Any commits to this file should be reviewed with security in mind. * + * Changes to this file can potentially create security vulnerabilities. * + * An approval from 2 Core members with history of modifying * + * this file is required. * + * * + * Does the change somehow allow for arbitrary javascript to be executed? * + * Or allows for someone to change the prototype of built-in objects? * + * Or gives undesired access to variables likes document or window? * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +var $sceMinErr = minErr('$sce'); + +var SCE_CONTEXTS = { + HTML: 'html', + CSS: 'css', + URL: 'url', + // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a + // url. (e.g. ng-include, script src, templateUrl) + RESOURCE_URL: 'resourceUrl', + JS: 'js' +}; + +// Helper functions follow. + +function adjustMatcher(matcher) { + if (matcher === 'self') { + return matcher; + } else if (isString(matcher)) { + // Strings match exactly except for 2 wildcards - '*' and '**'. + // '*' matches any character except those from the set ':/.?&'. + // '**' matches any character (like .* in a RegExp). + // More than 2 *'s raises an error as it's ill defined. + if (matcher.indexOf('***') > -1) { + throw $sceMinErr('iwcard', + 'Illegal sequence *** in string matcher. String: {0}', matcher); + } + matcher = escapeForRegexp(matcher). + replace('\\*\\*', '.*'). + replace('\\*', '[^:/.?&;]*'); + return new RegExp('^' + matcher + '$'); + } else if (isRegExp(matcher)) { + // The only other type of matcher allowed is a Regexp. + // Match entire URL / disallow partial matches. + // Flags are reset (i.e. no global, ignoreCase or multiline) + return new RegExp('^' + matcher.source + '$'); + } else { + throw $sceMinErr('imatcher', + 'Matchers may only be "self", string patterns or RegExp objects'); + } +} + + +function adjustMatchers(matchers) { + var adjustedMatchers = []; + if (isDefined(matchers)) { + forEach(matchers, function(matcher) { + adjustedMatchers.push(adjustMatcher(matcher)); + }); + } + return adjustedMatchers; +} + + +/** + * @ngdoc service + * @name $sceDelegate + * @kind function + * + * @description + * + * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict + * Contextual Escaping (SCE)} services to AngularJS. + * + * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of + * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is + * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to + * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things + * work because `$sce` delegates to `$sceDelegate` for these operations. + * + * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service. + * + * The default instance of `$sceDelegate` should work out of the box with little pain. While you + * can override it completely to change the behavior of `$sce`, the common case would + * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting + * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as + * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist + * $sceDelegateProvider.resourceUrlWhitelist} and {@link + * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} + */ + +/** + * @ngdoc provider + * @name $sceDelegateProvider + * @description + * + * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate + * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure + * that the URLs used for sourcing Angular templates are safe. Refer {@link + * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and + * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} + * + * For the general details about this service in Angular, read the main page for {@link ng.$sce + * Strict Contextual Escaping (SCE)}. + * + * **Example**: Consider the following case. + * + * - your app is hosted at url `http://myapp.example.com/` + * - but some of your templates are hosted on other domains you control such as + * `http://srv01.assets.example.com/`,  `http://srv02.assets.example.com/`, etc. + * - and you have an open redirect at `http://myapp.example.com/clickThru?...`. + * + * Here is what a secure configuration for this scenario might look like: + * + * ``` + * angular.module('myApp', []).config(function($sceDelegateProvider) { + * $sceDelegateProvider.resourceUrlWhitelist([ + * // Allow same origin resource loads. + * 'self', + * // Allow loading from our assets domain. Notice the difference between * and **. + * 'http://srv*.assets.example.com/**' + * ]); + * + * // The blacklist overrides the whitelist so the open redirect here is blocked. + * $sceDelegateProvider.resourceUrlBlacklist([ + * 'http://myapp.example.com/clickThru**' + * ]); + * }); + * ``` + */ + +function $SceDelegateProvider() { + this.SCE_CONTEXTS = SCE_CONTEXTS; + + // Resource URLs can also be trusted by policy. + var resourceUrlWhitelist = ['self'], + resourceUrlBlacklist = []; + + /** + * @ngdoc method + * @name $sceDelegateProvider#resourceUrlWhitelist + * @kind function + * + * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value + * provided. This must be an array or null. A snapshot of this array is used so further + * changes to the array are ignored. + * + * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items + * allowed in this array. + * + *
    + * **Note:** an empty whitelist array will block all URLs! + *
    + * + * @return {Array} the currently set whitelist array. + * + * The **default value** when no whitelist has been explicitly set is `['self']` allowing only + * same origin resource requests. + * + * @description + * Sets/Gets the whitelist of trusted resource URLs. + */ + this.resourceUrlWhitelist = function(value) { + if (arguments.length) { + resourceUrlWhitelist = adjustMatchers(value); + } + return resourceUrlWhitelist; + }; + + /** + * @ngdoc method + * @name $sceDelegateProvider#resourceUrlBlacklist + * @kind function + * + * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value + * provided. This must be an array or null. A snapshot of this array is used so further + * changes to the array are ignored. + * + * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items + * allowed in this array. + * + * The typical usage for the blacklist is to **block + * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as + * these would otherwise be trusted but actually return content from the redirected domain. + * + * Finally, **the blacklist overrides the whitelist** and has the final say. + * + * @return {Array} the currently set blacklist array. + * + * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there + * is no blacklist.) + * + * @description + * Sets/Gets the blacklist of trusted resource URLs. + */ + + this.resourceUrlBlacklist = function(value) { + if (arguments.length) { + resourceUrlBlacklist = adjustMatchers(value); + } + return resourceUrlBlacklist; + }; + + this.$get = ['$injector', function($injector) { + + var htmlSanitizer = function htmlSanitizer(html) { + throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.'); + }; + + if ($injector.has('$sanitize')) { + htmlSanitizer = $injector.get('$sanitize'); + } + + + function matchUrl(matcher, parsedUrl) { + if (matcher === 'self') { + return urlIsSameOrigin(parsedUrl); + } else { + // definitely a regex. See adjustMatchers() + return !!matcher.exec(parsedUrl.href); + } + } + + function isResourceUrlAllowedByPolicy(url) { + var parsedUrl = urlResolve(url.toString()); + var i, n, allowed = false; + // Ensure that at least one item from the whitelist allows this url. + for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) { + if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) { + allowed = true; + break; + } + } + if (allowed) { + // Ensure that no item from the blacklist blocked this url. + for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) { + if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) { + allowed = false; + break; + } + } + } + return allowed; + } + + function generateHolderType(Base) { + var holderType = function TrustedValueHolderType(trustedValue) { + this.$$unwrapTrustedValue = function() { + return trustedValue; + }; + }; + if (Base) { + holderType.prototype = new Base(); + } + holderType.prototype.valueOf = function sceValueOf() { + return this.$$unwrapTrustedValue(); + }; + holderType.prototype.toString = function sceToString() { + return this.$$unwrapTrustedValue().toString(); + }; + return holderType; + } + + var trustedValueHolderBase = generateHolderType(), + byType = {}; + + byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase); + byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase); + byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase); + byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase); + byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]); + + /** + * @ngdoc method + * @name $sceDelegate#trustAs + * + * @description + * Returns an object that is trusted by angular for use in specified strict + * contextual escaping contexts (such as ng-bind-html, ng-include, any src + * attribute interpolation, any dom event binding attribute interpolation + * such as for onclick, etc.) that uses the provided value. + * See {@link ng.$sce $sce} for enabling strict contextual escaping. + * + * @param {string} type The kind of context in which this value is safe for use. e.g. url, + * resourceUrl, html, js and css. + * @param {*} value The value that that should be considered trusted/safe. + * @returns {*} A value that can be used to stand in for the provided `value` in places + * where Angular expects a $sce.trustAs() return value. + */ + function trustAs(type, trustedValue) { + var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null); + if (!Constructor) { + throw $sceMinErr('icontext', + 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}', + type, trustedValue); + } + if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') { + return trustedValue; + } + // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting + // mutable objects, we ensure here that the value passed in is actually a string. + if (typeof trustedValue !== 'string') { + throw $sceMinErr('itype', + 'Attempted to trust a non-string value in a content requiring a string: Context: {0}', + type); + } + return new Constructor(trustedValue); + } + + /** + * @ngdoc method + * @name $sceDelegate#valueOf + * + * @description + * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs + * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link + * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. + * + * If the passed parameter is not a value that had been returned by {@link + * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is. + * + * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} + * call or anything else. + * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs + * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns + * `value` unchanged. + */ + function valueOf(maybeTrusted) { + if (maybeTrusted instanceof trustedValueHolderBase) { + return maybeTrusted.$$unwrapTrustedValue(); + } else { + return maybeTrusted; + } + } + + /** + * @ngdoc method + * @name $sceDelegate#getTrusted + * + * @description + * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and + * returns the originally supplied value if the queried context type is a supertype of the + * created type. If this condition isn't satisfied, throws an exception. + * + *
    + * Disabling auto-escaping is extremely dangerous, it usually creates a Cross Site Scripting + * (XSS) vulnerability in your application. + *
    + * + * @param {string} type The kind of context in which this value is to be used. + * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs + * `$sceDelegate.trustAs`} call. + * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs + * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception. + */ + function getTrusted(type, maybeTrusted) { + if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') { + return maybeTrusted; + } + var constructor = (byType.hasOwnProperty(type) ? byType[type] : null); + if (constructor && maybeTrusted instanceof constructor) { + return maybeTrusted.$$unwrapTrustedValue(); + } + // If we get here, then we may only take one of two actions. + // 1. sanitize the value for the requested type, or + // 2. throw an exception. + if (type === SCE_CONTEXTS.RESOURCE_URL) { + if (isResourceUrlAllowedByPolicy(maybeTrusted)) { + return maybeTrusted; + } else { + throw $sceMinErr('insecurl', + 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}', + maybeTrusted.toString()); + } + } else if (type === SCE_CONTEXTS.HTML) { + return htmlSanitizer(maybeTrusted); + } + throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.'); + } + + return { trustAs: trustAs, + getTrusted: getTrusted, + valueOf: valueOf }; + }]; +} + + +/** + * @ngdoc provider + * @name $sceProvider + * @description + * + * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service. + * - enable/disable Strict Contextual Escaping (SCE) in a module + * - override the default implementation with a custom delegate + * + * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}. + */ + +/* jshint maxlen: false*/ + +/** + * @ngdoc service + * @name $sce + * @kind function + * + * @description + * + * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS. + * + * # Strict Contextual Escaping + * + * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain + * contexts to result in a value that is marked as safe to use for that context. One example of + * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer + * to these contexts as privileged or SCE contexts. + * + * As of version 1.2, Angular ships with SCE enabled by default. + * + * Note: When enabled (the default), IE<11 in quirks mode is not supported. In this mode, IE<11 allow + * one to execute arbitrary javascript by the use of the expression() syntax. Refer + * to learn more about them. + * You can ensure your document is in standards mode and not quirks mode by adding `` + * to the top of your HTML document. + * + * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for + * security vulnerabilities such as XSS, clickjacking, etc. a lot easier. + * + * Here's an example of a binding in a privileged context: + * + * ``` + * + *
    + * ``` + * + * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE + * disabled, this application allows the user to render arbitrary HTML into the DIV. + * In a more realistic example, one may be rendering user comments, blog articles, etc. via + * bindings. (HTML is just one example of a context where rendering user controlled input creates + * security vulnerabilities.) + * + * For the case of HTML, you might use a library, either on the client side, or on the server side, + * to sanitize unsafe HTML before binding to the value and rendering it in the document. + * + * How would you ensure that every place that used these types of bindings was bound to a value that + * was sanitized by your library (or returned as safe for rendering by your server?) How can you + * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some + * properties/fields and forgot to update the binding to the sanitized value? + * + * To be secure by default, you want to ensure that any such bindings are disallowed unless you can + * determine that something explicitly says it's safe to use a value for binding in that + * context. You can then audit your code (a simple grep would do) to ensure that this is only done + * for those values that you can easily tell are safe - because they were received from your server, + * sanitized by your library, etc. You can organize your codebase to help with this - perhaps + * allowing only the files in a specific directory to do this. Ensuring that the internal API + * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task. + * + * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs} + * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to + * obtain values that will be accepted by SCE / privileged contexts. + * + * + * ## How does it work? + * + * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted + * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link + * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the + * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals. + * + * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link + * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly + * simplified): + * + * ``` + * var ngBindHtmlDirective = ['$sce', function($sce) { + * return function(scope, element, attr) { + * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) { + * element.html(value || ''); + * }); + * }; + * }]; + * ``` + * + * ## Impact on loading templates + * + * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as + * `templateUrl`'s specified by {@link guide/directive directives}. + * + * By default, Angular only loads templates from the same domain and protocol as the application + * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl + * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or + * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist + * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value. + * + * *Please note*: + * The browser's + * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest) + * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) + * policy apply in addition to this and may further restrict whether the template is successfully + * loaded. This means that without the right CORS policy, loading templates from a different domain + * won't work on all browsers. Also, loading templates from `file://` URL does not work on some + * browsers. + * + * ## This feels like too much overhead + * + * It's important to remember that SCE only applies to interpolation expressions. + * + * If your expressions are constant literals, they're automatically trusted and you don't need to + * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g. + * `
    `) just works. + * + * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them + * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here. + * + * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load + * templates in `ng-include` from your application's domain without having to even know about SCE. + * It blocks loading templates from other domains or loading templates over http from an https + * served document. You can change these by setting your own custom {@link + * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link + * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs. + * + * This significantly reduces the overhead. It is far easier to pay the small overhead and have an + * application that's secure and can be audited to verify that with much more ease than bolting + * security onto an application later. + * + * + * ## What trusted context types are supported? + * + * | Context | Notes | + * |---------------------|----------------| + * | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered and the {@link ngSanitize $sanitize} module is present this will sanitize the value instead of throwing an error. | + * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. | + * | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`
    Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. | + * | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently unused. Feel free to use it in your own directives. | + * + * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist}
    + * + * Each element in these arrays must be one of the following: + * + * - **'self'** + * - The special **string**, `'self'`, can be used to match against all URLs of the **same + * domain** as the application document using the **same protocol**. + * - **String** (except the special value `'self'`) + * - The string is matched against the full *normalized / absolute URL* of the resource + * being tested (substring matches are not good enough.) + * - There are exactly **two wildcard sequences** - `*` and `**`. All other characters + * match themselves. + * - `*`: matches zero or more occurrences of any character other than one of the following 6 + * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'. It's a useful wildcard for use + * in a whitelist. + * - `**`: matches zero or more occurrences of *any* character. As such, it's not + * appropriate for use in a scheme, domain, etc. as it would match too much. (e.g. + * http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might + * not have been the intention.) Its usage at the very end of the path is ok. (e.g. + * http://foo.example.com/templates/**). + * - **RegExp** (*see caveat below*) + * - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax + * (and all the inevitable escaping) makes them *harder to maintain*. It's easy to + * accidentally introduce a bug when one updates a complex expression (imho, all regexes should + * have good test coverage). For instance, the use of `.` in the regex is correct only in a + * small number of cases. A `.` character in the regex used when matching the scheme or a + * subdomain could be matched against a `:` or literal `.` that was likely not intended. It + * is highly recommended to use the string patterns and only fall back to regular expressions + * as a last resort. + * - The regular expression must be an instance of RegExp (i.e. not a string.) It is + * matched against the **entire** *normalized / absolute URL* of the resource being tested + * (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags + * present on the RegExp (such as multiline, global, ignoreCase) are ignored. + * - If you are generating your JavaScript from some other templating engine (not + * recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)), + * remember to escape your regular expression (and be aware that you might need more than + * one level of escaping depending on your templating engine and the way you interpolated + * the value.) Do make use of your platform's escaping mechanism as it might be good + * enough before coding your own. E.g. Ruby has + * [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape) + * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape). + * Javascript lacks a similar built in function for escaping. Take a look at Google + * Closure library's [goog.string.regExpEscape(s)]( + * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962). + * + * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example. + * + * ## Show me an example using SCE. + * + * + * + *
    + *

    + * User comments
    + * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when + * $sanitize is available. If $sanitize isn't available, this results in an error instead of an + * exploit. + *
    + *
    + * {{userComment.name}}: + * + *
    + *
    + *
    + *
    + *
    + * + * + * angular.module('mySceApp', ['ngSanitize']) + * .controller('AppController', ['$http', '$templateCache', '$sce', + * function($http, $templateCache, $sce) { + * var self = this; + * $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) { + * self.userComments = userComments; + * }); + * self.explicitlyTrustedHtml = $sce.trustAsHtml( + * 'Hover over this text.'); + * }]); + * + * + * + * [ + * { "name": "Alice", + * "htmlComment": + * "Is anyone reading this?" + * }, + * { "name": "Bob", + * "htmlComment": "Yes! Am I the only other one?" + * } + * ] + * + * + * + * describe('SCE doc demo', function() { + * it('should sanitize untrusted values', function() { + * expect(element.all(by.css('.htmlComment')).first().getInnerHtml()) + * .toBe('Is anyone reading this?'); + * }); + * + * it('should NOT sanitize explicitly trusted values', function() { + * expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe( + * 'Hover over this text.'); + * }); + * }); + * + *
    + * + * + * + * ## Can I disable SCE completely? + * + * Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits + * for little coding overhead. It will be much harder to take an SCE disabled application and + * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE + * for cases where you have a lot of existing code that was written before SCE was introduced and + * you're migrating them a module at a time. + * + * That said, here's how you can completely disable SCE: + * + * ``` + * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) { + * // Completely disable SCE. For demonstration purposes only! + * // Do not use in new projects. + * $sceProvider.enabled(false); + * }); + * ``` + * + */ +/* jshint maxlen: 100 */ + +function $SceProvider() { + var enabled = true; + + /** + * @ngdoc method + * @name $sceProvider#enabled + * @kind function + * + * @param {boolean=} value If provided, then enables/disables SCE. + * @return {boolean} true if SCE is enabled, false otherwise. + * + * @description + * Enables/disables SCE and returns the current value. + */ + this.enabled = function(value) { + if (arguments.length) { + enabled = !!value; + } + return enabled; + }; + + + /* Design notes on the default implementation for SCE. + * + * The API contract for the SCE delegate + * ------------------------------------- + * The SCE delegate object must provide the following 3 methods: + * + * - trustAs(contextEnum, value) + * This method is used to tell the SCE service that the provided value is OK to use in the + * contexts specified by contextEnum. It must return an object that will be accepted by + * getTrusted() for a compatible contextEnum and return this value. + * + * - valueOf(value) + * For values that were not produced by trustAs(), return them as is. For values that were + * produced by trustAs(), return the corresponding input value to trustAs. Basically, if + * trustAs is wrapping the given values into some type, this operation unwraps it when given + * such a value. + * + * - getTrusted(contextEnum, value) + * This function should return the a value that is safe to use in the context specified by + * contextEnum or throw and exception otherwise. + * + * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be + * opaque or wrapped in some holder object. That happens to be an implementation detail. For + * instance, an implementation could maintain a registry of all trusted objects by context. In + * such a case, trustAs() would return the same object that was passed in. getTrusted() would + * return the same object passed in if it was found in the registry under a compatible context or + * throw an exception otherwise. An implementation might only wrap values some of the time based + * on some criteria. getTrusted() might return a value and not throw an exception for special + * constants or objects even if not wrapped. All such implementations fulfill this contract. + * + * + * A note on the inheritance model for SCE contexts + * ------------------------------------------------ + * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This + * is purely an implementation details. + * + * The contract is simply this: + * + * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value) + * will also succeed. + * + * Inheritance happens to capture this in a natural way. In some future, we + * may not use inheritance anymore. That is OK because no code outside of + * sce.js and sceSpecs.js would need to be aware of this detail. + */ + + this.$get = ['$parse', '$sceDelegate', function( + $parse, $sceDelegate) { + // Prereq: Ensure that we're not running in IE<11 quirks mode. In that mode, IE < 11 allow + // the "expression(javascript expression)" syntax which is insecure. + if (enabled && msie < 8) { + throw $sceMinErr('iequirks', + 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' + + 'mode. You can fix this by adding the text to the top of your HTML ' + + 'document. See http://docs.angularjs.org/api/ng.$sce for more information.'); + } + + var sce = shallowCopy(SCE_CONTEXTS); + + /** + * @ngdoc method + * @name $sce#isEnabled + * @kind function + * + * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you + * have to do it at module config time on {@link ng.$sceProvider $sceProvider}. + * + * @description + * Returns a boolean indicating if SCE is enabled. + */ + sce.isEnabled = function() { + return enabled; + }; + sce.trustAs = $sceDelegate.trustAs; + sce.getTrusted = $sceDelegate.getTrusted; + sce.valueOf = $sceDelegate.valueOf; + + if (!enabled) { + sce.trustAs = sce.getTrusted = function(type, value) { return value; }; + sce.valueOf = identity; + } + + /** + * @ngdoc method + * @name $sce#parseAs + * + * @description + * Converts Angular {@link guide/expression expression} into a function. This is like {@link + * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it + * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*, + * *result*)} + * + * @param {string} type The kind of SCE context in which this result will be used. + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + */ + sce.parseAs = function sceParseAs(type, expr) { + var parsed = $parse(expr); + if (parsed.literal && parsed.constant) { + return parsed; + } else { + return $parse(expr, function(value) { + return sce.getTrusted(type, value); + }); + } + }; + + /** + * @ngdoc method + * @name $sce#trustAs + * + * @description + * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such, + * returns an object that is trusted by angular for use in specified strict contextual + * escaping contexts (such as ng-bind-html, ng-include, any src attribute + * interpolation, any dom event binding attribute interpolation such as for onclick, etc.) + * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual + * escaping. + * + * @param {string} type The kind of context in which this value is safe for use. e.g. url, + * resourceUrl, html, js and css. + * @param {*} value The value that that should be considered trusted/safe. + * @returns {*} A value that can be used to stand in for the provided `value` in places + * where Angular expects a $sce.trustAs() return value. + */ + + /** + * @ngdoc method + * @name $sce#trustAsHtml + * + * @description + * Shorthand method. `$sce.trustAsHtml(value)` → + * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`} + * + * @param {*} value The value to trustAs. + * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml + * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives + * only accept expressions that are either literal constants or are the + * return value of {@link ng.$sce#trustAs $sce.trustAs}.) + */ + + /** + * @ngdoc method + * @name $sce#trustAsUrl + * + * @description + * Shorthand method. `$sce.trustAsUrl(value)` → + * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`} + * + * @param {*} value The value to trustAs. + * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl + * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives + * only accept expressions that are either literal constants or are the + * return value of {@link ng.$sce#trustAs $sce.trustAs}.) + */ + + /** + * @ngdoc method + * @name $sce#trustAsResourceUrl + * + * @description + * Shorthand method. `$sce.trustAsResourceUrl(value)` → + * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`} + * + * @param {*} value The value to trustAs. + * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl + * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives + * only accept expressions that are either literal constants or are the return + * value of {@link ng.$sce#trustAs $sce.trustAs}.) + */ + + /** + * @ngdoc method + * @name $sce#trustAsJs + * + * @description + * Shorthand method. `$sce.trustAsJs(value)` → + * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`} + * + * @param {*} value The value to trustAs. + * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs + * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives + * only accept expressions that are either literal constants or are the + * return value of {@link ng.$sce#trustAs $sce.trustAs}.) + */ + + /** + * @ngdoc method + * @name $sce#getTrusted + * + * @description + * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such, + * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the + * originally supplied value if the queried context type is a supertype of the created type. + * If this condition isn't satisfied, throws an exception. + * + * @param {string} type The kind of context in which this value is to be used. + * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`} + * call. + * @returns {*} The value the was originally provided to + * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context. + * Otherwise, throws an exception. + */ + + /** + * @ngdoc method + * @name $sce#getTrustedHtml + * + * @description + * Shorthand method. `$sce.getTrustedHtml(value)` → + * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`} + * + * @param {*} value The value to pass to `$sce.getTrusted`. + * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)` + */ + + /** + * @ngdoc method + * @name $sce#getTrustedCss + * + * @description + * Shorthand method. `$sce.getTrustedCss(value)` → + * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`} + * + * @param {*} value The value to pass to `$sce.getTrusted`. + * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)` + */ + + /** + * @ngdoc method + * @name $sce#getTrustedUrl + * + * @description + * Shorthand method. `$sce.getTrustedUrl(value)` → + * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`} + * + * @param {*} value The value to pass to `$sce.getTrusted`. + * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)` + */ + + /** + * @ngdoc method + * @name $sce#getTrustedResourceUrl + * + * @description + * Shorthand method. `$sce.getTrustedResourceUrl(value)` → + * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`} + * + * @param {*} value The value to pass to `$sceDelegate.getTrusted`. + * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)` + */ + + /** + * @ngdoc method + * @name $sce#getTrustedJs + * + * @description + * Shorthand method. `$sce.getTrustedJs(value)` → + * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`} + * + * @param {*} value The value to pass to `$sce.getTrusted`. + * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)` + */ + + /** + * @ngdoc method + * @name $sce#parseAsHtml + * + * @description + * Shorthand method. `$sce.parseAsHtml(expression string)` → + * {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`} + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + */ + + /** + * @ngdoc method + * @name $sce#parseAsCss + * + * @description + * Shorthand method. `$sce.parseAsCss(value)` → + * {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`} + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + */ + + /** + * @ngdoc method + * @name $sce#parseAsUrl + * + * @description + * Shorthand method. `$sce.parseAsUrl(value)` → + * {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`} + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + */ + + /** + * @ngdoc method + * @name $sce#parseAsResourceUrl + * + * @description + * Shorthand method. `$sce.parseAsResourceUrl(value)` → + * {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`} + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + */ + + /** + * @ngdoc method + * @name $sce#parseAsJs + * + * @description + * Shorthand method. `$sce.parseAsJs(value)` → + * {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`} + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + */ + + // Shorthand delegations. + var parse = sce.parseAs, + getTrusted = sce.getTrusted, + trustAs = sce.trustAs; + + forEach(SCE_CONTEXTS, function(enumValue, name) { + var lName = lowercase(name); + sce[camelCase("parse_as_" + lName)] = function(expr) { + return parse(enumValue, expr); + }; + sce[camelCase("get_trusted_" + lName)] = function(value) { + return getTrusted(enumValue, value); + }; + sce[camelCase("trust_as_" + lName)] = function(value) { + return trustAs(enumValue, value); + }; + }); + + return sce; + }]; +} + +/** + * !!! This is an undocumented "private" service !!! + * + * @name $sniffer + * @requires $window + * @requires $document + * + * @property {boolean} history Does the browser support html5 history api ? + * @property {boolean} transitions Does the browser support CSS transition events ? + * @property {boolean} animations Does the browser support CSS animation events ? + * + * @description + * This is very simple implementation of testing browser's features. + */ +function $SnifferProvider() { + this.$get = ['$window', '$document', function($window, $document) { + var eventSupport = {}, + // Chrome Packaged Apps are not allowed to access `history.pushState`. They can be detected by + // the presence of `chrome.app.runtime` (see https://developer.chrome.com/apps/api_index) + isChromePackagedApp = $window.chrome && $window.chrome.app && $window.chrome.app.runtime, + hasHistoryPushState = !isChromePackagedApp && $window.history && $window.history.pushState, + android = + toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]), + boxee = /Boxee/i.test(($window.navigator || {}).userAgent), + document = $document[0] || {}, + vendorPrefix, + vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/, + bodyStyle = document.body && document.body.style, + transitions = false, + animations = false, + match; + + if (bodyStyle) { + for (var prop in bodyStyle) { + if (match = vendorRegex.exec(prop)) { + vendorPrefix = match[0]; + vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1); + break; + } + } + + if (!vendorPrefix) { + vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit'; + } + + transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle)); + animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle)); + + if (android && (!transitions || !animations)) { + transitions = isString(bodyStyle.webkitTransition); + animations = isString(bodyStyle.webkitAnimation); + } + } + + + return { + // Android has history.pushState, but it does not update location correctly + // so let's not use the history API at all. + // http://code.google.com/p/android/issues/detail?id=17471 + // https://github.com/angular/angular.js/issues/904 + + // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has + // so let's not use the history API also + // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined + // jshint -W018 + history: !!(hasHistoryPushState && !(android < 4) && !boxee), + // jshint +W018 + hasEvent: function(event) { + // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have + // it. In particular the event is not fired when backspace or delete key are pressed or + // when cut operation is performed. + // IE10+ implements 'input' event but it erroneously fires under various situations, + // e.g. when placeholder changes, or a form is focused. + if (event === 'input' && msie <= 11) return false; + + if (isUndefined(eventSupport[event])) { + var divElm = document.createElement('div'); + eventSupport[event] = 'on' + event in divElm; + } + + return eventSupport[event]; + }, + csp: csp(), + vendorPrefix: vendorPrefix, + transitions: transitions, + animations: animations, + android: android + }; + }]; +} + +var $templateRequestMinErr = minErr('$compile'); + +/** + * @ngdoc provider + * @name $templateRequestProvider + * @description + * Used to configure the options passed to the {@link $http} service when making a template request. + * + * For example, it can be used for specifying the "Accept" header that is sent to the server, when + * requesting a template. + */ +function $TemplateRequestProvider() { + + var httpOptions; + + /** + * @ngdoc method + * @name $templateRequestProvider#httpOptions + * @description + * The options to be passed to the {@link $http} service when making the request. + * You can use this to override options such as the "Accept" header for template requests. + * + * The {@link $templateRequest} will set the `cache` and the `transformResponse` properties of the + * options if not overridden here. + * + * @param {string=} value new value for the {@link $http} options. + * @returns {string|self} Returns the {@link $http} options when used as getter and self if used as setter. + */ + this.httpOptions = function(val) { + if (val) { + httpOptions = val; + return this; + } + return httpOptions; + }; + + /** + * @ngdoc service + * @name $templateRequest + * + * @description + * The `$templateRequest` service runs security checks then downloads the provided template using + * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request + * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the + * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the + * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted + * when `tpl` is of type string and `$templateCache` has the matching entry. + * + * If you want to pass custom options to the `$http` service, such as setting the Accept header you + * can configure this via {@link $templateRequestProvider#httpOptions}. + * + * @param {string|TrustedResourceUrl} tpl The HTTP request template URL + * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty + * + * @return {Promise} a promise for the HTTP response data of the given URL. + * + * @property {number} totalPendingRequests total amount of pending template requests being downloaded. + */ + this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) { + + function handleRequestFn(tpl, ignoreRequestError) { + handleRequestFn.totalPendingRequests++; + + // We consider the template cache holds only trusted templates, so + // there's no need to go through whitelisting again for keys that already + // are included in there. This also makes Angular accept any script + // directive, no matter its name. However, we still need to unwrap trusted + // types. + if (!isString(tpl) || !$templateCache.get(tpl)) { + tpl = $sce.getTrustedResourceUrl(tpl); + } + + var transformResponse = $http.defaults && $http.defaults.transformResponse; + + if (isArray(transformResponse)) { + transformResponse = transformResponse.filter(function(transformer) { + return transformer !== defaultHttpResponseTransform; + }); + } else if (transformResponse === defaultHttpResponseTransform) { + transformResponse = null; + } + + return $http.get(tpl, extend({ + cache: $templateCache, + transformResponse: transformResponse + }, httpOptions)) + ['finally'](function() { + handleRequestFn.totalPendingRequests--; + }) + .then(function(response) { + $templateCache.put(tpl, response.data); + return response.data; + }, handleError); + + function handleError(resp) { + if (!ignoreRequestError) { + throw $templateRequestMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})', + tpl, resp.status, resp.statusText); + } + return $q.reject(resp); + } + } + + handleRequestFn.totalPendingRequests = 0; + + return handleRequestFn; + }]; +} + +function $$TestabilityProvider() { + this.$get = ['$rootScope', '$browser', '$location', + function($rootScope, $browser, $location) { + + /** + * @name $testability + * + * @description + * The private $$testability service provides a collection of methods for use when debugging + * or by automated test and debugging tools. + */ + var testability = {}; + + /** + * @name $$testability#findBindings + * + * @description + * Returns an array of elements that are bound (via ng-bind or {{}}) + * to expressions matching the input. + * + * @param {Element} element The element root to search from. + * @param {string} expression The binding expression to match. + * @param {boolean} opt_exactMatch If true, only returns exact matches + * for the expression. Filters and whitespace are ignored. + */ + testability.findBindings = function(element, expression, opt_exactMatch) { + var bindings = element.getElementsByClassName('ng-binding'); + var matches = []; + forEach(bindings, function(binding) { + var dataBinding = angular.element(binding).data('$binding'); + if (dataBinding) { + forEach(dataBinding, function(bindingName) { + if (opt_exactMatch) { + var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)'); + if (matcher.test(bindingName)) { + matches.push(binding); + } + } else { + if (bindingName.indexOf(expression) != -1) { + matches.push(binding); + } + } + }); + } + }); + return matches; + }; + + /** + * @name $$testability#findModels + * + * @description + * Returns an array of elements that are two-way found via ng-model to + * expressions matching the input. + * + * @param {Element} element The element root to search from. + * @param {string} expression The model expression to match. + * @param {boolean} opt_exactMatch If true, only returns exact matches + * for the expression. + */ + testability.findModels = function(element, expression, opt_exactMatch) { + var prefixes = ['ng-', 'data-ng-', 'ng\\:']; + for (var p = 0; p < prefixes.length; ++p) { + var attributeEquals = opt_exactMatch ? '=' : '*='; + var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]'; + var elements = element.querySelectorAll(selector); + if (elements.length) { + return elements; + } + } + }; + + /** + * @name $$testability#getLocation + * + * @description + * Shortcut for getting the location in a browser agnostic way. Returns + * the path, search, and hash. (e.g. /path?a=b#hash) + */ + testability.getLocation = function() { + return $location.url(); + }; + + /** + * @name $$testability#setLocation + * + * @description + * Shortcut for navigating to a location without doing a full page reload. + * + * @param {string} url The location url (path, search and hash, + * e.g. /path?a=b#hash) to go to. + */ + testability.setLocation = function(url) { + if (url !== $location.url()) { + $location.url(url); + $rootScope.$digest(); + } + }; + + /** + * @name $$testability#whenStable + * + * @description + * Calls the callback when $timeout and $http requests are completed. + * + * @param {function} callback + */ + testability.whenStable = function(callback) { + $browser.notifyWhenNoOutstandingRequests(callback); + }; + + return testability; + }]; +} + +function $TimeoutProvider() { + this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler', + function($rootScope, $browser, $q, $$q, $exceptionHandler) { + + var deferreds = {}; + + + /** + * @ngdoc service + * @name $timeout + * + * @description + * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch + * block and delegates any exceptions to + * {@link ng.$exceptionHandler $exceptionHandler} service. + * + * The return value of calling `$timeout` is a promise, which will be resolved when + * the delay has passed and the timeout function, if provided, is executed. + * + * To cancel a timeout request, call `$timeout.cancel(promise)`. + * + * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to + * synchronously flush the queue of deferred functions. + * + * If you only want a promise that will be resolved after some specified delay + * then you can call `$timeout` without the `fn` function. + * + * @param {function()=} fn A function, whose execution should be delayed. + * @param {number=} [delay=0] Delay in milliseconds. + * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise + * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. + * @param {...*=} Pass additional parameters to the executed function. + * @returns {Promise} Promise that will be resolved when the timeout is reached. The promise + * will be resolved with the return value of the `fn` function. + * + */ + function timeout(fn, delay, invokeApply) { + if (!isFunction(fn)) { + invokeApply = delay; + delay = fn; + fn = noop; + } + + var args = sliceArgs(arguments, 3), + skipApply = (isDefined(invokeApply) && !invokeApply), + deferred = (skipApply ? $$q : $q).defer(), + promise = deferred.promise, + timeoutId; + + timeoutId = $browser.defer(function() { + try { + deferred.resolve(fn.apply(null, args)); + } catch (e) { + deferred.reject(e); + $exceptionHandler(e); + } + finally { + delete deferreds[promise.$$timeoutId]; + } + + if (!skipApply) $rootScope.$apply(); + }, delay); + + promise.$$timeoutId = timeoutId; + deferreds[timeoutId] = deferred; + + return promise; + } + + + /** + * @ngdoc method + * @name $timeout#cancel + * + * @description + * Cancels a task associated with the `promise`. As a result of this, the promise will be + * resolved with a rejection. + * + * @param {Promise=} promise Promise returned by the `$timeout` function. + * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully + * canceled. + */ + timeout.cancel = function(promise) { + if (promise && promise.$$timeoutId in deferreds) { + deferreds[promise.$$timeoutId].reject('canceled'); + delete deferreds[promise.$$timeoutId]; + return $browser.defer.cancel(promise.$$timeoutId); + } + return false; + }; + + return timeout; + }]; +} + +// NOTE: The usage of window and document instead of $window and $document here is +// deliberate. This service depends on the specific behavior of anchor nodes created by the +// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and +// cause us to break tests. In addition, when the browser resolves a URL for XHR, it +// doesn't know about mocked locations and resolves URLs to the real document - which is +// exactly the behavior needed here. There is little value is mocking these out for this +// service. +var urlParsingNode = document.createElement("a"); +var originUrl = urlResolve(window.location.href); + + +/** + * + * Implementation Notes for non-IE browsers + * ---------------------------------------- + * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM, + * results both in the normalizing and parsing of the URL. Normalizing means that a relative + * URL will be resolved into an absolute URL in the context of the application document. + * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related + * properties are all populated to reflect the normalized URL. This approach has wide + * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See + * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html + * + * Implementation Notes for IE + * --------------------------- + * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other + * browsers. However, the parsed components will not be set if the URL assigned did not specify + * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We + * work around that by performing the parsing in a 2nd step by taking a previously normalized + * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the + * properties such as protocol, hostname, port, etc. + * + * References: + * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement + * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html + * http://url.spec.whatwg.org/#urlutils + * https://github.com/angular/angular.js/pull/2902 + * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/ + * + * @kind function + * @param {string} url The URL to be parsed. + * @description Normalizes and parses a URL. + * @returns {object} Returns the normalized URL as a dictionary. + * + * | member name | Description | + * |---------------|----------------| + * | href | A normalized version of the provided URL if it was not an absolute URL | + * | protocol | The protocol including the trailing colon | + * | host | The host and port (if the port is non-default) of the normalizedUrl | + * | search | The search params, minus the question mark | + * | hash | The hash string, minus the hash symbol + * | hostname | The hostname + * | port | The port, without ":" + * | pathname | The pathname, beginning with "/" + * + */ +function urlResolve(url) { + var href = url; + + if (msie) { + // Normalize before parse. Refer Implementation Notes on why this is + // done in two steps on IE. + urlParsingNode.setAttribute("href", href); + href = urlParsingNode.href; + } + + urlParsingNode.setAttribute('href', href); + + // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils + return { + href: urlParsingNode.href, + protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '', + host: urlParsingNode.host, + search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '', + hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '', + hostname: urlParsingNode.hostname, + port: urlParsingNode.port, + pathname: (urlParsingNode.pathname.charAt(0) === '/') + ? urlParsingNode.pathname + : '/' + urlParsingNode.pathname + }; +} + +/** + * Parse a request URL and determine whether this is a same-origin request as the application document. + * + * @param {string|object} requestUrl The url of the request as a string that will be resolved + * or a parsed URL object. + * @returns {boolean} Whether the request is for the same origin as the application document. + */ +function urlIsSameOrigin(requestUrl) { + var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl; + return (parsed.protocol === originUrl.protocol && + parsed.host === originUrl.host); +} + +/** + * @ngdoc service + * @name $window + * + * @description + * A reference to the browser's `window` object. While `window` + * is globally available in JavaScript, it causes testability problems, because + * it is a global variable. In angular we always refer to it through the + * `$window` service, so it may be overridden, removed or mocked for testing. + * + * Expressions, like the one defined for the `ngClick` directive in the example + * below, are evaluated with respect to the current scope. Therefore, there is + * no risk of inadvertently coding in a dependency on a global value in such an + * expression. + * + * @example + + + +
    + + +
    +
    + + it('should display the greeting in the input box', function() { + element(by.model('greeting')).sendKeys('Hello, E2E Tests'); + // If we click the button it will block the test runner + // element(':button').click(); + }); + +
    + */ +function $WindowProvider() { + this.$get = valueFn(window); +} + +/** + * @name $$cookieReader + * @requires $document + * + * @description + * This is a private service for reading cookies used by $http and ngCookies + * + * @return {Object} a key/value map of the current cookies + */ +function $$CookieReader($document) { + var rawDocument = $document[0] || {}; + var lastCookies = {}; + var lastCookieString = ''; + + function safeDecodeURIComponent(str) { + try { + return decodeURIComponent(str); + } catch (e) { + return str; + } + } + + return function() { + var cookieArray, cookie, i, index, name; + var currentCookieString = rawDocument.cookie || ''; + + if (currentCookieString !== lastCookieString) { + lastCookieString = currentCookieString; + cookieArray = lastCookieString.split('; '); + lastCookies = {}; + + for (i = 0; i < cookieArray.length; i++) { + cookie = cookieArray[i]; + index = cookie.indexOf('='); + if (index > 0) { //ignore nameless cookies + name = safeDecodeURIComponent(cookie.substring(0, index)); + // the first value that is seen for a cookie is the most + // specific one. values for the same cookie name that + // follow are for less specific paths. + if (isUndefined(lastCookies[name])) { + lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1)); + } + } + } + } + return lastCookies; + }; +} + +$$CookieReader.$inject = ['$document']; + +function $$CookieReaderProvider() { + this.$get = $$CookieReader; +} + +/* global currencyFilter: true, + dateFilter: true, + filterFilter: true, + jsonFilter: true, + limitToFilter: true, + lowercaseFilter: true, + numberFilter: true, + orderByFilter: true, + uppercaseFilter: true, + */ + +/** + * @ngdoc provider + * @name $filterProvider + * @description + * + * Filters are just functions which transform input to an output. However filters need to be + * Dependency Injected. To achieve this a filter definition consists of a factory function which is + * annotated with dependencies and is responsible for creating a filter function. + * + *
    + * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`. + * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace + * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores + * (`myapp_subsection_filterx`). + *
    + * + * ```js + * // Filter registration + * function MyModule($provide, $filterProvider) { + * // create a service to demonstrate injection (not always needed) + * $provide.value('greet', function(name){ + * return 'Hello ' + name + '!'; + * }); + * + * // register a filter factory which uses the + * // greet service to demonstrate DI. + * $filterProvider.register('greet', function(greet){ + * // return the filter function which uses the greet service + * // to generate salutation + * return function(text) { + * // filters need to be forgiving so check input validity + * return text && greet(text) || text; + * }; + * }); + * } + * ``` + * + * The filter function is registered with the `$injector` under the filter name suffix with + * `Filter`. + * + * ```js + * it('should be the same instance', inject( + * function($filterProvider) { + * $filterProvider.register('reverse', function(){ + * return ...; + * }); + * }, + * function($filter, reverseFilter) { + * expect($filter('reverse')).toBe(reverseFilter); + * }); + * ``` + * + * + * For more information about how angular filters work, and how to create your own filters, see + * {@link guide/filter Filters} in the Angular Developer Guide. + */ + +/** + * @ngdoc service + * @name $filter + * @kind function + * @description + * Filters are used for formatting data displayed to the user. + * + * The general syntax in templates is as follows: + * + * {{ expression [| filter_name[:parameter_value] ... ] }} + * + * @param {String} name Name of the filter function to retrieve + * @return {Function} the filter function + * @example + + +
    +

    {{ originalText }}

    +

    {{ filteredText }}

    +
    +
    + + + angular.module('filterExample', []) + .controller('MainCtrl', function($scope, $filter) { + $scope.originalText = 'hello'; + $scope.filteredText = $filter('uppercase')($scope.originalText); + }); + +
    + */ +$FilterProvider.$inject = ['$provide']; +function $FilterProvider($provide) { + var suffix = 'Filter'; + + /** + * @ngdoc method + * @name $filterProvider#register + * @param {string|Object} name Name of the filter function, or an object map of filters where + * the keys are the filter names and the values are the filter factories. + * + *
    + * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`. + * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace + * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores + * (`myapp_subsection_filterx`). + *
    + * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered. + * @returns {Object} Registered filter instance, or if a map of filters was provided then a map + * of the registered filter instances. + */ + function register(name, factory) { + if (isObject(name)) { + var filters = {}; + forEach(name, function(filter, key) { + filters[key] = register(key, filter); + }); + return filters; + } else { + return $provide.factory(name + suffix, factory); + } + } + this.register = register; + + this.$get = ['$injector', function($injector) { + return function(name) { + return $injector.get(name + suffix); + }; + }]; + + //////////////////////////////////////// + + /* global + currencyFilter: false, + dateFilter: false, + filterFilter: false, + jsonFilter: false, + limitToFilter: false, + lowercaseFilter: false, + numberFilter: false, + orderByFilter: false, + uppercaseFilter: false, + */ + + register('currency', currencyFilter); + register('date', dateFilter); + register('filter', filterFilter); + register('json', jsonFilter); + register('limitTo', limitToFilter); + register('lowercase', lowercaseFilter); + register('number', numberFilter); + register('orderBy', orderByFilter); + register('uppercase', uppercaseFilter); +} + +/** + * @ngdoc filter + * @name filter + * @kind function + * + * @description + * Selects a subset of items from `array` and returns it as a new array. + * + * @param {Array} array The source array. + * @param {string|Object|function()} expression The predicate to be used for selecting items from + * `array`. + * + * Can be one of: + * + * - `string`: The string is used for matching against the contents of the `array`. All strings or + * objects with string properties in `array` that match this string will be returned. This also + * applies to nested object properties. + * The predicate can be negated by prefixing the string with `!`. + * + * - `Object`: A pattern object can be used to filter specific properties on objects contained + * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items + * which have property `name` containing "M" and property `phone` containing "1". A special + * property name `$` can be used (as in `{$:"text"}`) to accept a match against any + * property of the object or its nested object properties. That's equivalent to the simple + * substring match with a `string` as described above. The predicate can be negated by prefixing + * the string with `!`. + * For example `{name: "!M"}` predicate will return an array of items which have property `name` + * not containing "M". + * + * Note that a named property will match properties on the same level only, while the special + * `$` property will match properties on the same level or deeper. E.g. an array item like + * `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but + * **will** be matched by `{$: 'John'}`. + * + * - `function(value, index, array)`: A predicate function can be used to write arbitrary filters. + * The function is called for each element of the array, with the element, its index, and + * the entire array itself as arguments. + * + * The final result is an array of those elements that the predicate returned true for. + * + * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in + * determining if the expected value (from the filter expression) and actual value (from + * the object in the array) should be considered a match. + * + * Can be one of: + * + * - `function(actual, expected)`: + * The function will be given the object value and the predicate value to compare and + * should return true if both values should be considered equal. + * + * - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`. + * This is essentially strict comparison of expected and actual. + * + * - `false|undefined`: A short hand for a function which will look for a substring match in case + * insensitive way. + * + * Primitive values are converted to strings. Objects are not compared against primitives, + * unless they have a custom `toString` method (e.g. `Date` objects). + * + * @example + + +
    + + + + + + + + +
    NamePhone
    {{friend.name}}{{friend.phone}}
    +
    +
    +
    +
    +
    + + + + + + +
    NamePhone
    {{friendObj.name}}{{friendObj.phone}}
    +
    + + var expectFriendNames = function(expectedNames, key) { + element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) { + arr.forEach(function(wd, i) { + expect(wd.getText()).toMatch(expectedNames[i]); + }); + }); + }; + + it('should search across all fields when filtering with a string', function() { + var searchText = element(by.model('searchText')); + searchText.clear(); + searchText.sendKeys('m'); + expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend'); + + searchText.clear(); + searchText.sendKeys('76'); + expectFriendNames(['John', 'Julie'], 'friend'); + }); + + it('should search in specific fields when filtering with a predicate object', function() { + var searchAny = element(by.model('search.$')); + searchAny.clear(); + searchAny.sendKeys('i'); + expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj'); + }); + it('should use a equal comparison when comparator is true', function() { + var searchName = element(by.model('search.name')); + var strict = element(by.model('strict')); + searchName.clear(); + searchName.sendKeys('Julie'); + strict.click(); + expectFriendNames(['Julie'], 'friendObj'); + }); + +
    + */ +function filterFilter() { + return function(array, expression, comparator) { + if (!isArrayLike(array)) { + if (array == null) { + return array; + } else { + throw minErr('filter')('notarray', 'Expected array but received: {0}', array); + } + } + + var expressionType = getTypeForFilter(expression); + var predicateFn; + var matchAgainstAnyProp; + + switch (expressionType) { + case 'function': + predicateFn = expression; + break; + case 'boolean': + case 'null': + case 'number': + case 'string': + matchAgainstAnyProp = true; + //jshint -W086 + case 'object': + //jshint +W086 + predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp); + break; + default: + return array; + } + + return Array.prototype.filter.call(array, predicateFn); + }; +} + +// Helper functions for `filterFilter` +function createPredicateFn(expression, comparator, matchAgainstAnyProp) { + var shouldMatchPrimitives = isObject(expression) && ('$' in expression); + var predicateFn; + + if (comparator === true) { + comparator = equals; + } else if (!isFunction(comparator)) { + comparator = function(actual, expected) { + if (isUndefined(actual)) { + // No substring matching against `undefined` + return false; + } + if ((actual === null) || (expected === null)) { + // No substring matching against `null`; only match against `null` + return actual === expected; + } + if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) { + // Should not compare primitives against objects, unless they have custom `toString` method + return false; + } + + actual = lowercase('' + actual); + expected = lowercase('' + expected); + return actual.indexOf(expected) !== -1; + }; + } + + predicateFn = function(item) { + if (shouldMatchPrimitives && !isObject(item)) { + return deepCompare(item, expression.$, comparator, false); + } + return deepCompare(item, expression, comparator, matchAgainstAnyProp); + }; + + return predicateFn; +} + +function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) { + var actualType = getTypeForFilter(actual); + var expectedType = getTypeForFilter(expected); + + if ((expectedType === 'string') && (expected.charAt(0) === '!')) { + return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp); + } else if (isArray(actual)) { + // In case `actual` is an array, consider it a match + // if ANY of it's items matches `expected` + return actual.some(function(item) { + return deepCompare(item, expected, comparator, matchAgainstAnyProp); + }); + } + + switch (actualType) { + case 'object': + var key; + if (matchAgainstAnyProp) { + for (key in actual) { + if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) { + return true; + } + } + return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false); + } else if (expectedType === 'object') { + for (key in expected) { + var expectedVal = expected[key]; + if (isFunction(expectedVal) || isUndefined(expectedVal)) { + continue; + } + + var matchAnyProperty = key === '$'; + var actualVal = matchAnyProperty ? actual : actual[key]; + if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) { + return false; + } + } + return true; + } else { + return comparator(actual, expected); + } + break; + case 'function': + return false; + default: + return comparator(actual, expected); + } +} + +// Used for easily differentiating between `null` and actual `object` +function getTypeForFilter(val) { + return (val === null) ? 'null' : typeof val; +} + +var MAX_DIGITS = 22; +var DECIMAL_SEP = '.'; +var ZERO_CHAR = '0'; + +/** + * @ngdoc filter + * @name currency + * @kind function + * + * @description + * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default + * symbol for current locale is used. + * + * @param {number} amount Input to filter. + * @param {string=} symbol Currency symbol or identifier to be displayed. + * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale + * @returns {string} Formatted number. + * + * + * @example + + + +
    +
    + default currency symbol ($): {{amount | currency}}
    + custom currency identifier (USD$): {{amount | currency:"USD$"}} + no fractions (0): {{amount | currency:"USD$":0}} +
    +
    + + it('should init with 1234.56', function() { + expect(element(by.id('currency-default')).getText()).toBe('$1,234.56'); + expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56'); + expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235'); + }); + it('should update', function() { + if (browser.params.browser == 'safari') { + // Safari does not understand the minus key. See + // https://github.com/angular/protractor/issues/481 + return; + } + element(by.model('amount')).clear(); + element(by.model('amount')).sendKeys('-1234'); + expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00'); + expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00'); + expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234'); + }); + +
    + */ +currencyFilter.$inject = ['$locale']; +function currencyFilter($locale) { + var formats = $locale.NUMBER_FORMATS; + return function(amount, currencySymbol, fractionSize) { + if (isUndefined(currencySymbol)) { + currencySymbol = formats.CURRENCY_SYM; + } + + if (isUndefined(fractionSize)) { + fractionSize = formats.PATTERNS[1].maxFrac; + } + + // if null or undefined pass it through + return (amount == null) + ? amount + : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize). + replace(/\u00A4/g, currencySymbol); + }; +} + +/** + * @ngdoc filter + * @name number + * @kind function + * + * @description + * Formats a number as text. + * + * If the input is null or undefined, it will just be returned. + * If the input is infinite (Infinity or -Infinity), the Infinity symbol '∞' or '-∞' is returned, respectively. + * If the input is not a number an empty string is returned. + * + * + * @param {number|string} number Number to format. + * @param {(number|string)=} fractionSize Number of decimal places to round the number to. + * If this is not provided then the fraction size is computed from the current locale's number + * formatting pattern. In the case of the default locale, it will be 3. + * @returns {string} Number rounded to fractionSize and places a “,” after each third digit. + * + * @example + + + +
    +
    + Default formatting: {{val | number}}
    + No fractions: {{val | number:0}}
    + Negative number: {{-val | number:4}} +
    +
    + + it('should format numbers', function() { + expect(element(by.id('number-default')).getText()).toBe('1,234.568'); + expect(element(by.binding('val | number:0')).getText()).toBe('1,235'); + expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679'); + }); + + it('should update', function() { + element(by.model('val')).clear(); + element(by.model('val')).sendKeys('3374.333'); + expect(element(by.id('number-default')).getText()).toBe('3,374.333'); + expect(element(by.binding('val | number:0')).getText()).toBe('3,374'); + expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330'); + }); + +
    + */ +numberFilter.$inject = ['$locale']; +function numberFilter($locale) { + var formats = $locale.NUMBER_FORMATS; + return function(number, fractionSize) { + + // if null or undefined pass it through + return (number == null) + ? number + : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP, + fractionSize); + }; +} + +/** + * Parse a number (as a string) into three components that can be used + * for formatting the number. + * + * (Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/) + * + * @param {string} numStr The number to parse + * @return {object} An object describing this number, containing the following keys: + * - d : an array of digits containing leading zeros as necessary + * - i : the number of the digits in `d` that are to the left of the decimal point + * - e : the exponent for numbers that would need more than `MAX_DIGITS` digits in `d` + * + */ +function parse(numStr) { + var exponent = 0, digits, numberOfIntegerDigits; + var i, j, zeros; + + // Decimal point? + if ((numberOfIntegerDigits = numStr.indexOf(DECIMAL_SEP)) > -1) { + numStr = numStr.replace(DECIMAL_SEP, ''); + } + + // Exponential form? + if ((i = numStr.search(/e/i)) > 0) { + // Work out the exponent. + if (numberOfIntegerDigits < 0) numberOfIntegerDigits = i; + numberOfIntegerDigits += +numStr.slice(i + 1); + numStr = numStr.substring(0, i); + } else if (numberOfIntegerDigits < 0) { + // There was no decimal point or exponent so it is an integer. + numberOfIntegerDigits = numStr.length; + } + + // Count the number of leading zeros. + for (i = 0; numStr.charAt(i) == ZERO_CHAR; i++) {/* jshint noempty: false */} + + if (i == (zeros = numStr.length)) { + // The digits are all zero. + digits = [0]; + numberOfIntegerDigits = 1; + } else { + // Count the number of trailing zeros + zeros--; + while (numStr.charAt(zeros) == ZERO_CHAR) zeros--; + + // Trailing zeros are insignificant so ignore them + numberOfIntegerDigits -= i; + digits = []; + // Convert string to array of digits without leading/trailing zeros. + for (j = 0; i <= zeros; i++, j++) { + digits[j] = +numStr.charAt(i); + } + } + + // If the number overflows the maximum allowed digits then use an exponent. + if (numberOfIntegerDigits > MAX_DIGITS) { + digits = digits.splice(0, MAX_DIGITS - 1); + exponent = numberOfIntegerDigits - 1; + numberOfIntegerDigits = 1; + } + + return { d: digits, e: exponent, i: numberOfIntegerDigits }; +} + +/** + * Round the parsed number to the specified number of decimal places + * This function changed the parsedNumber in-place + */ +function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) { + var digits = parsedNumber.d; + var fractionLen = digits.length - parsedNumber.i; + + // determine fractionSize if it is not specified; `+fractionSize` converts it to a number + fractionSize = (isUndefined(fractionSize)) ? Math.min(Math.max(minFrac, fractionLen), maxFrac) : +fractionSize; + + // The index of the digit to where rounding is to occur + var roundAt = fractionSize + parsedNumber.i; + var digit = digits[roundAt]; + + if (roundAt > 0) { + // Drop fractional digits beyond `roundAt` + digits.splice(Math.max(parsedNumber.i, roundAt)); + + // Set non-fractional digits beyond `roundAt` to 0 + for (var j = roundAt; j < digits.length; j++) { + digits[j] = 0; + } + } else { + // We rounded to zero so reset the parsedNumber + fractionLen = Math.max(0, fractionLen); + parsedNumber.i = 1; + digits.length = Math.max(1, roundAt = fractionSize + 1); + digits[0] = 0; + for (var i = 1; i < roundAt; i++) digits[i] = 0; + } + + if (digit >= 5) { + if (roundAt - 1 < 0) { + for (var k = 0; k > roundAt; k--) { + digits.unshift(0); + parsedNumber.i++; + } + digits.unshift(1); + parsedNumber.i++; + } else { + digits[roundAt - 1]++; + } + } + + // Pad out with zeros to get the required fraction length + for (; fractionLen < Math.max(0, fractionSize); fractionLen++) digits.push(0); + + + // Do any carrying, e.g. a digit was rounded up to 10 + var carry = digits.reduceRight(function(carry, d, i, digits) { + d = d + carry; + digits[i] = d % 10; + return Math.floor(d / 10); + }, 0); + if (carry) { + digits.unshift(carry); + parsedNumber.i++; + } +} + +/** + * Format a number into a string + * @param {number} number The number to format + * @param {{ + * minFrac, // the minimum number of digits required in the fraction part of the number + * maxFrac, // the maximum number of digits required in the fraction part of the number + * gSize, // number of digits in each group of separated digits + * lgSize, // number of digits in the last group of digits before the decimal separator + * negPre, // the string to go in front of a negative number (e.g. `-` or `(`)) + * posPre, // the string to go in front of a positive number + * negSuf, // the string to go after a negative number (e.g. `)`) + * posSuf // the string to go after a positive number + * }} pattern + * @param {string} groupSep The string to separate groups of number (e.g. `,`) + * @param {string} decimalSep The string to act as the decimal separator (e.g. `.`) + * @param {[type]} fractionSize The size of the fractional part of the number + * @return {string} The number formatted as a string + */ +function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) { + + if (!(isString(number) || isNumber(number)) || isNaN(number)) return ''; + + var isInfinity = !isFinite(number); + var isZero = false; + var numStr = Math.abs(number) + '', + formattedText = '', + parsedNumber; + + if (isInfinity) { + formattedText = '\u221e'; + } else { + parsedNumber = parse(numStr); + + roundNumber(parsedNumber, fractionSize, pattern.minFrac, pattern.maxFrac); + + var digits = parsedNumber.d; + var integerLen = parsedNumber.i; + var exponent = parsedNumber.e; + var decimals = []; + isZero = digits.reduce(function(isZero, d) { return isZero && !d; }, true); + + // pad zeros for small numbers + while (integerLen < 0) { + digits.unshift(0); + integerLen++; + } + + // extract decimals digits + if (integerLen > 0) { + decimals = digits.splice(integerLen); + } else { + decimals = digits; + digits = [0]; + } + + // format the integer digits with grouping separators + var groups = []; + if (digits.length >= pattern.lgSize) { + groups.unshift(digits.splice(-pattern.lgSize).join('')); + } + while (digits.length > pattern.gSize) { + groups.unshift(digits.splice(-pattern.gSize).join('')); + } + if (digits.length) { + groups.unshift(digits.join('')); + } + formattedText = groups.join(groupSep); + + // append the decimal digits + if (decimals.length) { + formattedText += decimalSep + decimals.join(''); + } + + if (exponent) { + formattedText += 'e+' + exponent; + } + } + if (number < 0 && !isZero) { + return pattern.negPre + formattedText + pattern.negSuf; + } else { + return pattern.posPre + formattedText + pattern.posSuf; + } +} + +function padNumber(num, digits, trim, negWrap) { + var neg = ''; + if (num < 0 || (negWrap && num <= 0)) { + if (negWrap) { + num = -num + 1; + } else { + num = -num; + neg = '-'; + } + } + num = '' + num; + while (num.length < digits) num = ZERO_CHAR + num; + if (trim) { + num = num.substr(num.length - digits); + } + return neg + num; +} + + +function dateGetter(name, size, offset, trim, negWrap) { + offset = offset || 0; + return function(date) { + var value = date['get' + name](); + if (offset > 0 || value > -offset) { + value += offset; + } + if (value === 0 && offset == -12) value = 12; + return padNumber(value, size, trim, negWrap); + }; +} + +function dateStrGetter(name, shortForm, standAlone) { + return function(date, formats) { + var value = date['get' + name](); + var propPrefix = (standAlone ? 'STANDALONE' : '') + (shortForm ? 'SHORT' : ''); + var get = uppercase(propPrefix + name); + + return formats[get][value]; + }; +} + +function timeZoneGetter(date, formats, offset) { + var zone = -1 * offset; + var paddedZone = (zone >= 0) ? "+" : ""; + + paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) + + padNumber(Math.abs(zone % 60), 2); + + return paddedZone; +} + +function getFirstThursdayOfYear(year) { + // 0 = index of January + var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay(); + // 4 = index of Thursday (+1 to account for 1st = 5) + // 11 = index of *next* Thursday (+1 account for 1st = 12) + return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst); +} + +function getThursdayThisWeek(datetime) { + return new Date(datetime.getFullYear(), datetime.getMonth(), + // 4 = index of Thursday + datetime.getDate() + (4 - datetime.getDay())); +} + +function weekGetter(size) { + return function(date) { + var firstThurs = getFirstThursdayOfYear(date.getFullYear()), + thisThurs = getThursdayThisWeek(date); + + var diff = +thisThurs - +firstThurs, + result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week + + return padNumber(result, size); + }; +} + +function ampmGetter(date, formats) { + return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1]; +} + +function eraGetter(date, formats) { + return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1]; +} + +function longEraGetter(date, formats) { + return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1]; +} + +var DATE_FORMATS = { + yyyy: dateGetter('FullYear', 4, 0, false, true), + yy: dateGetter('FullYear', 2, 0, true, true), + y: dateGetter('FullYear', 1, 0, false, true), + MMMM: dateStrGetter('Month'), + MMM: dateStrGetter('Month', true), + MM: dateGetter('Month', 2, 1), + M: dateGetter('Month', 1, 1), + LLLL: dateStrGetter('Month', false, true), + dd: dateGetter('Date', 2), + d: dateGetter('Date', 1), + HH: dateGetter('Hours', 2), + H: dateGetter('Hours', 1), + hh: dateGetter('Hours', 2, -12), + h: dateGetter('Hours', 1, -12), + mm: dateGetter('Minutes', 2), + m: dateGetter('Minutes', 1), + ss: dateGetter('Seconds', 2), + s: dateGetter('Seconds', 1), + // while ISO 8601 requires fractions to be prefixed with `.` or `,` + // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions + sss: dateGetter('Milliseconds', 3), + EEEE: dateStrGetter('Day'), + EEE: dateStrGetter('Day', true), + a: ampmGetter, + Z: timeZoneGetter, + ww: weekGetter(2), + w: weekGetter(1), + G: eraGetter, + GG: eraGetter, + GGG: eraGetter, + GGGG: longEraGetter +}; + +var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/, + NUMBER_STRING = /^\-?\d+$/; + +/** + * @ngdoc filter + * @name date + * @kind function + * + * @description + * Formats `date` to a string based on the requested `format`. + * + * `format` string can be composed of the following elements: + * + * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010) + * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) + * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199) + * * `'MMMM'`: Month in year (January-December) + * * `'MMM'`: Month in year (Jan-Dec) + * * `'MM'`: Month in year, padded (01-12) + * * `'M'`: Month in year (1-12) + * * `'LLLL'`: Stand-alone month in year (January-December) + * * `'dd'`: Day in month, padded (01-31) + * * `'d'`: Day in month (1-31) + * * `'EEEE'`: Day in Week,(Sunday-Saturday) + * * `'EEE'`: Day in Week, (Sun-Sat) + * * `'HH'`: Hour in day, padded (00-23) + * * `'H'`: Hour in day (0-23) + * * `'hh'`: Hour in AM/PM, padded (01-12) + * * `'h'`: Hour in AM/PM, (1-12) + * * `'mm'`: Minute in hour, padded (00-59) + * * `'m'`: Minute in hour (0-59) + * * `'ss'`: Second in minute, padded (00-59) + * * `'s'`: Second in minute (0-59) + * * `'sss'`: Millisecond in second, padded (000-999) + * * `'a'`: AM/PM marker + * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200) + * * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year + * * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year + * * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD') + * * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini') + * + * `format` string can also be one of the following predefined + * {@link guide/i18n localizable formats}: + * + * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale + * (e.g. Sep 3, 2010 12:05:08 PM) + * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 PM) + * * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US locale + * (e.g. Friday, September 3, 2010) + * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010) + * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010) + * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10) + * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM) + * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM) + * + * `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g. + * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence + * (e.g. `"h 'o''clock'"`). + * + * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or + * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its + * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is + * specified in the string input, the time is considered to be in the local timezone. + * @param {string=} format Formatting rules (see Description). If not specified, + * `mediumDate` is used. + * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the + * continental US time zone abbreviations, but for general use, use a time zone offset, for + * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian) + * If not specified, the timezone of the browser will be used. + * @returns {string} Formatted string or the input if input is not recognized as date/millis. + * + * @example + + + {{1288323623006 | date:'medium'}}: + {{1288323623006 | date:'medium'}}
    + {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}: + {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}
    + {{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}: + {{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}
    + {{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}: + {{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}
    +
    + + it('should format date', function() { + expect(element(by.binding("1288323623006 | date:'medium'")).getText()). + toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/); + expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()). + toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/); + expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()). + toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/); + expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()). + toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/); + }); + +
    + */ +dateFilter.$inject = ['$locale']; +function dateFilter($locale) { + + + var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; + // 1 2 3 4 5 6 7 8 9 10 11 + function jsonStringToDate(string) { + var match; + if (match = string.match(R_ISO8601_STR)) { + var date = new Date(0), + tzHour = 0, + tzMin = 0, + dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear, + timeSetter = match[8] ? date.setUTCHours : date.setHours; + + if (match[9]) { + tzHour = toInt(match[9] + match[10]); + tzMin = toInt(match[9] + match[11]); + } + dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3])); + var h = toInt(match[4] || 0) - tzHour; + var m = toInt(match[5] || 0) - tzMin; + var s = toInt(match[6] || 0); + var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000); + timeSetter.call(date, h, m, s, ms); + return date; + } + return string; + } + + + return function(date, format, timezone) { + var text = '', + parts = [], + fn, match; + + format = format || 'mediumDate'; + format = $locale.DATETIME_FORMATS[format] || format; + if (isString(date)) { + date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date); + } + + if (isNumber(date)) { + date = new Date(date); + } + + if (!isDate(date) || !isFinite(date.getTime())) { + return date; + } + + while (format) { + match = DATE_FORMATS_SPLIT.exec(format); + if (match) { + parts = concat(parts, match, 1); + format = parts.pop(); + } else { + parts.push(format); + format = null; + } + } + + var dateTimezoneOffset = date.getTimezoneOffset(); + if (timezone) { + dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset); + date = convertTimezoneToLocal(date, timezone, true); + } + forEach(parts, function(value) { + fn = DATE_FORMATS[value]; + text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset) + : value === "''" ? "'" : value.replace(/(^'|'$)/g, '').replace(/''/g, "'"); + }); + + return text; + }; +} + + +/** + * @ngdoc filter + * @name json + * @kind function + * + * @description + * Allows you to convert a JavaScript object into JSON string. + * + * This filter is mostly useful for debugging. When using the double curly {{value}} notation + * the binding is automatically converted to JSON. + * + * @param {*} object Any JavaScript object (including arrays and primitive types) to filter. + * @param {number=} spacing The number of spaces to use per indentation, defaults to 2. + * @returns {string} JSON string. + * + * + * @example + + +
    {{ {'name':'value'} | json }}
    +
    {{ {'name':'value'} | json:4 }}
    +
    + + it('should jsonify filtered objects', function() { + expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/); + expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/); + }); + +
    + * + */ +function jsonFilter() { + return function(object, spacing) { + if (isUndefined(spacing)) { + spacing = 2; + } + return toJson(object, spacing); + }; +} + + +/** + * @ngdoc filter + * @name lowercase + * @kind function + * @description + * Converts string to lowercase. + * @see angular.lowercase + */ +var lowercaseFilter = valueFn(lowercase); + + +/** + * @ngdoc filter + * @name uppercase + * @kind function + * @description + * Converts string to uppercase. + * @see angular.uppercase + */ +var uppercaseFilter = valueFn(uppercase); + +/** + * @ngdoc filter + * @name limitTo + * @kind function + * + * @description + * Creates a new array or string containing only a specified number of elements. The elements + * are taken from either the beginning or the end of the source array, string or number, as specified by + * the value and sign (positive or negative) of `limit`. If a number is used as input, it is + * converted to a string. + * + * @param {Array|string|number} input Source array, string or number to be limited. + * @param {string|number} limit The length of the returned array or string. If the `limit` number + * is positive, `limit` number of items from the beginning of the source array/string are copied. + * If the number is negative, `limit` number of items from the end of the source array/string + * are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined, + * the input will be returned unchanged. + * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin` + * indicates an offset from the end of `input`. Defaults to `0`. + * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array + * had less than `limit` elements. + * + * @example + + + +
    + +

    Output numbers: {{ numbers | limitTo:numLimit }}

    + +

    Output letters: {{ letters | limitTo:letterLimit }}

    + +

    Output long number: {{ longNumber | limitTo:longNumberLimit }}

    +
    +
    + + var numLimitInput = element(by.model('numLimit')); + var letterLimitInput = element(by.model('letterLimit')); + var longNumberLimitInput = element(by.model('longNumberLimit')); + var limitedNumbers = element(by.binding('numbers | limitTo:numLimit')); + var limitedLetters = element(by.binding('letters | limitTo:letterLimit')); + var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit')); + + it('should limit the number array to first three items', function() { + expect(numLimitInput.getAttribute('value')).toBe('3'); + expect(letterLimitInput.getAttribute('value')).toBe('3'); + expect(longNumberLimitInput.getAttribute('value')).toBe('3'); + expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]'); + expect(limitedLetters.getText()).toEqual('Output letters: abc'); + expect(limitedLongNumber.getText()).toEqual('Output long number: 234'); + }); + + // There is a bug in safari and protractor that doesn't like the minus key + // it('should update the output when -3 is entered', function() { + // numLimitInput.clear(); + // numLimitInput.sendKeys('-3'); + // letterLimitInput.clear(); + // letterLimitInput.sendKeys('-3'); + // longNumberLimitInput.clear(); + // longNumberLimitInput.sendKeys('-3'); + // expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]'); + // expect(limitedLetters.getText()).toEqual('Output letters: ghi'); + // expect(limitedLongNumber.getText()).toEqual('Output long number: 342'); + // }); + + it('should not exceed the maximum size of input array', function() { + numLimitInput.clear(); + numLimitInput.sendKeys('100'); + letterLimitInput.clear(); + letterLimitInput.sendKeys('100'); + longNumberLimitInput.clear(); + longNumberLimitInput.sendKeys('100'); + expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]'); + expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi'); + expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342'); + }); + +
    +*/ +function limitToFilter() { + return function(input, limit, begin) { + if (Math.abs(Number(limit)) === Infinity) { + limit = Number(limit); + } else { + limit = toInt(limit); + } + if (isNaN(limit)) return input; + + if (isNumber(input)) input = input.toString(); + if (!isArray(input) && !isString(input)) return input; + + begin = (!begin || isNaN(begin)) ? 0 : toInt(begin); + begin = (begin < 0) ? Math.max(0, input.length + begin) : begin; + + if (limit >= 0) { + return input.slice(begin, begin + limit); + } else { + if (begin === 0) { + return input.slice(limit, input.length); + } else { + return input.slice(Math.max(0, begin + limit), begin); + } + } + }; +} + +/** + * @ngdoc filter + * @name orderBy + * @kind function + * + * @description + * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically + * for strings and numerically for numbers. Note: if you notice numbers are not being sorted + * as expected, make sure they are actually being saved as numbers and not strings. + * Array-like values (e.g. NodeLists, jQuery objects, TypedArrays, Strings, etc) are also supported. + * + * @param {Array} array The array (or array-like object) to sort. + * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be + * used by the comparator to determine the order of elements. + * + * Can be one of: + * + * - `function`: Getter function. The result of this function will be sorted using the + * `<`, `===`, `>` operator. + * - `string`: An Angular expression. The result of this expression is used to compare elements + * (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by + * 3 first characters of a property called `name`). The result of a constant expression + * is interpreted as a property name to be used in comparisons (for example `"special name"` + * to sort object by the value of their `special name` property). An expression can be + * optionally prefixed with `+` or `-` to control ascending or descending sort order + * (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array + * element itself is used to compare where sorting. + * - `Array`: An array of function or string predicates. The first predicate in the array + * is used for sorting, but when two items are equivalent, the next predicate is used. + * + * If the predicate is missing or empty then it defaults to `'+'`. + * + * @param {boolean=} reverse Reverse the order of the array. + * @returns {Array} Sorted copy of the source array. + * + * + * @example + * The example below demonstrates a simple ngRepeat, where the data is sorted + * by age in descending order (predicate is set to `'-age'`). + * `reverse` is not set, which means it defaults to `false`. + + +
    + + + + + + + + + + + +
    NamePhone NumberAge
    {{friend.name}}{{friend.phone}}{{friend.age}}
    +
    +
    + + angular.module('orderByExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.friends = + [{name:'John', phone:'555-1212', age:10}, + {name:'Mary', phone:'555-9876', age:19}, + {name:'Mike', phone:'555-4321', age:21}, + {name:'Adam', phone:'555-5678', age:35}, + {name:'Julie', phone:'555-8765', age:29}]; + }]); + +
    + * + * The predicate and reverse parameters can be controlled dynamically through scope properties, + * as shown in the next example. + * @example + + +
    +
    Sorting predicate = {{predicate}}; reverse = {{reverse}}
    +
    + + + + + + + + + + + + +
    + + + + + + + + +
    {{friend.name}}{{friend.phone}}{{friend.age}}
    +
    +
    + + angular.module('orderByExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.friends = + [{name:'John', phone:'555-1212', age:10}, + {name:'Mary', phone:'555-9876', age:19}, + {name:'Mike', phone:'555-4321', age:21}, + {name:'Adam', phone:'555-5678', age:35}, + {name:'Julie', phone:'555-8765', age:29}]; + $scope.predicate = 'age'; + $scope.reverse = true; + $scope.order = function(predicate) { + $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false; + $scope.predicate = predicate; + }; + }]); + + + .sortorder:after { + content: '\25b2'; + } + .sortorder.reverse:after { + content: '\25bc'; + } + +
    + * + * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the + * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the + * desired parameters. + * + * Example: + * + * @example + + +
    +
    Sorting predicate = {{predicate}}; reverse = {{reverse}}
    + + + + + + + + + + + +
    + + + + + + + + +
    {{friend.name}}{{friend.phone}}{{friend.age}}
    +
    +
    + + + angular.module('orderByExample', []) + .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) { + var orderBy = $filter('orderBy'); + $scope.friends = [ + { name: 'John', phone: '555-1212', age: 10 }, + { name: 'Mary', phone: '555-9876', age: 19 }, + { name: 'Mike', phone: '555-4321', age: 21 }, + { name: 'Adam', phone: '555-5678', age: 35 }, + { name: 'Julie', phone: '555-8765', age: 29 } + ]; + $scope.order = function(predicate) { + $scope.predicate = predicate; + $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false; + $scope.friends = orderBy($scope.friends, predicate, $scope.reverse); + }; + $scope.order('age', true); + }]); + + + + .sortorder:after { + content: '\25b2'; + } + .sortorder.reverse:after { + content: '\25bc'; + } + +
    + */ +orderByFilter.$inject = ['$parse']; +function orderByFilter($parse) { + return function(array, sortPredicate, reverseOrder) { + + if (array == null) return array; + if (!isArrayLike(array)) { + throw minErr('orderBy')('notarray', 'Expected array but received: {0}', array); + } + + if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; } + if (sortPredicate.length === 0) { sortPredicate = ['+']; } + + var predicates = processPredicates(sortPredicate, reverseOrder); + // Add a predicate at the end that evaluates to the element index. This makes the + // sort stable as it works as a tie-breaker when all the input predicates cannot + // distinguish between two elements. + predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1}); + + // The next three lines are a version of a Swartzian Transform idiom from Perl + // (sometimes called the Decorate-Sort-Undecorate idiom) + // See https://en.wikipedia.org/wiki/Schwartzian_transform + var compareValues = Array.prototype.map.call(array, getComparisonObject); + compareValues.sort(doComparison); + array = compareValues.map(function(item) { return item.value; }); + + return array; + + function getComparisonObject(value, index) { + return { + value: value, + predicateValues: predicates.map(function(predicate) { + return getPredicateValue(predicate.get(value), index); + }) + }; + } + + function doComparison(v1, v2) { + var result = 0; + for (var index=0, length = predicates.length; index < length; ++index) { + result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending; + if (result) break; + } + return result; + } + }; + + function processPredicates(sortPredicate, reverseOrder) { + reverseOrder = reverseOrder ? -1 : 1; + return sortPredicate.map(function(predicate) { + var descending = 1, get = identity; + + if (isFunction(predicate)) { + get = predicate; + } else if (isString(predicate)) { + if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) { + descending = predicate.charAt(0) == '-' ? -1 : 1; + predicate = predicate.substring(1); + } + if (predicate !== '') { + get = $parse(predicate); + if (get.constant) { + var key = get(); + get = function(value) { return value[key]; }; + } + } + } + return { get: get, descending: descending * reverseOrder }; + }); + } + + function isPrimitive(value) { + switch (typeof value) { + case 'number': /* falls through */ + case 'boolean': /* falls through */ + case 'string': + return true; + default: + return false; + } + } + + function objectValue(value, index) { + // If `valueOf` is a valid function use that + if (typeof value.valueOf === 'function') { + value = value.valueOf(); + if (isPrimitive(value)) return value; + } + // If `toString` is a valid function and not the one from `Object.prototype` use that + if (hasCustomToString(value)) { + value = value.toString(); + if (isPrimitive(value)) return value; + } + // We have a basic object so we use the position of the object in the collection + return index; + } + + function getPredicateValue(value, index) { + var type = typeof value; + if (value === null) { + type = 'string'; + value = 'null'; + } else if (type === 'string') { + value = value.toLowerCase(); + } else if (type === 'object') { + value = objectValue(value, index); + } + return { value: value, type: type }; + } + + function compare(v1, v2) { + var result = 0; + if (v1.type === v2.type) { + if (v1.value !== v2.value) { + result = v1.value < v2.value ? -1 : 1; + } + } else { + result = v1.type < v2.type ? -1 : 1; + } + return result; + } +} + +function ngDirective(directive) { + if (isFunction(directive)) { + directive = { + link: directive + }; + } + directive.restrict = directive.restrict || 'AC'; + return valueFn(directive); +} + +/** + * @ngdoc directive + * @name a + * @restrict E + * + * @description + * Modifies the default behavior of the html A tag so that the default action is prevented when + * the href attribute is empty. + * + * This change permits the easy creation of action links with the `ngClick` directive + * without changing the location or causing page reloads, e.g.: + * `Add Item` + */ +var htmlAnchorDirective = valueFn({ + restrict: 'E', + compile: function(element, attr) { + if (!attr.href && !attr.xlinkHref) { + return function(scope, element) { + // If the linked element is not an anchor tag anymore, do nothing + if (element[0].nodeName.toLowerCase() !== 'a') return; + + // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute. + var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ? + 'xlink:href' : 'href'; + element.on('click', function(event) { + // if we have no href url, then don't navigate anywhere. + if (!element.attr(href)) { + event.preventDefault(); + } + }); + }; + } + } +}); + +/** + * @ngdoc directive + * @name ngHref + * @restrict A + * @priority 99 + * + * @description + * Using Angular markup like `{{hash}}` in an href attribute will + * make the link go to the wrong URL if the user clicks it before + * Angular has a chance to replace the `{{hash}}` markup with its + * value. Until Angular replaces the markup the link will be broken + * and will most likely return a 404 error. The `ngHref` directive + * solves this problem. + * + * The wrong way to write it: + * ```html + * link1 + * ``` + * + * The correct way to write it: + * ```html + * link1 + * ``` + * + * @element A + * @param {template} ngHref any string which can contain `{{}}` markup. + * + * @example + * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes + * in links and their different behaviors: + + +
    + link 1 (link, don't reload)
    + link 2 (link, don't reload)
    + link 3 (link, reload!)
    + anchor (link, don't reload)
    + anchor (no link)
    + link (link, change location) +
    + + it('should execute ng-click but not reload when href without value', function() { + element(by.id('link-1')).click(); + expect(element(by.model('value')).getAttribute('value')).toEqual('1'); + expect(element(by.id('link-1')).getAttribute('href')).toBe(''); + }); + + it('should execute ng-click but not reload when href empty string', function() { + element(by.id('link-2')).click(); + expect(element(by.model('value')).getAttribute('value')).toEqual('2'); + expect(element(by.id('link-2')).getAttribute('href')).toBe(''); + }); + + it('should execute ng-click and change url when ng-href specified', function() { + expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/); + + element(by.id('link-3')).click(); + + // At this point, we navigate away from an Angular page, so we need + // to use browser.driver to get the base webdriver. + + browser.wait(function() { + return browser.driver.getCurrentUrl().then(function(url) { + return url.match(/\/123$/); + }); + }, 5000, 'page should navigate to /123'); + }); + + it('should execute ng-click but not reload when href empty string and name specified', function() { + element(by.id('link-4')).click(); + expect(element(by.model('value')).getAttribute('value')).toEqual('4'); + expect(element(by.id('link-4')).getAttribute('href')).toBe(''); + }); + + it('should execute ng-click but not reload when no href but name specified', function() { + element(by.id('link-5')).click(); + expect(element(by.model('value')).getAttribute('value')).toEqual('5'); + expect(element(by.id('link-5')).getAttribute('href')).toBe(null); + }); + + it('should only change url when only ng-href', function() { + element(by.model('value')).clear(); + element(by.model('value')).sendKeys('6'); + expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/); + + element(by.id('link-6')).click(); + + // At this point, we navigate away from an Angular page, so we need + // to use browser.driver to get the base webdriver. + browser.wait(function() { + return browser.driver.getCurrentUrl().then(function(url) { + return url.match(/\/6$/); + }); + }, 5000, 'page should navigate to /6'); + }); + +
    + */ + +/** + * @ngdoc directive + * @name ngSrc + * @restrict A + * @priority 99 + * + * @description + * Using Angular markup like `{{hash}}` in a `src` attribute doesn't + * work right: The browser will fetch from the URL with the literal + * text `{{hash}}` until Angular replaces the expression inside + * `{{hash}}`. The `ngSrc` directive solves this problem. + * + * The buggy way to write it: + * ```html + * Description + * ``` + * + * The correct way to write it: + * ```html + * Description + * ``` + * + * @element IMG + * @param {template} ngSrc any string which can contain `{{}}` markup. + */ + +/** + * @ngdoc directive + * @name ngSrcset + * @restrict A + * @priority 99 + * + * @description + * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't + * work right: The browser will fetch from the URL with the literal + * text `{{hash}}` until Angular replaces the expression inside + * `{{hash}}`. The `ngSrcset` directive solves this problem. + * + * The buggy way to write it: + * ```html + * Description + * ``` + * + * The correct way to write it: + * ```html + * Description + * ``` + * + * @element IMG + * @param {template} ngSrcset any string which can contain `{{}}` markup. + */ + +/** + * @ngdoc directive + * @name ngDisabled + * @restrict A + * @priority 100 + * + * @description + * + * This directive sets the `disabled` attribute on the element if the + * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy. + * + * A special directive is necessary because we cannot use interpolation inside the `disabled` + * attribute. See the {@link guide/interpolation interpolation guide} for more info. + * + * @example + + +
    + +
    + + it('should toggle button', function() { + expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy(); + element(by.model('checked')).click(); + expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy(); + }); + +
    + * + * @element INPUT + * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy, + * then the `disabled` attribute will be set on the element + */ + + +/** + * @ngdoc directive + * @name ngChecked + * @restrict A + * @priority 100 + * + * @description + * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy. + * + * Note that this directive should not be used together with {@link ngModel `ngModel`}, + * as this can lead to unexpected behavior. + * + * A special directive is necessary because we cannot use interpolation inside the `checked` + * attribute. See the {@link guide/interpolation interpolation guide} for more info. + * + * @example + + +
    + +
    + + it('should check both checkBoxes', function() { + expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy(); + element(by.model('master')).click(); + expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy(); + }); + +
    + * + * @element INPUT + * @param {expression} ngChecked If the {@link guide/expression expression} is truthy, + * then the `checked` attribute will be set on the element + */ + + +/** + * @ngdoc directive + * @name ngReadonly + * @restrict A + * @priority 100 + * + * @description + * + * Sets the `readOnly` attribute on the element, if the expression inside `ngReadonly` is truthy. + * + * A special directive is necessary because we cannot use interpolation inside the `readOnly` + * attribute. See the {@link guide/interpolation interpolation guide} for more info. + * + * @example + + +
    + +
    + + it('should toggle readonly attr', function() { + expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy(); + element(by.model('checked')).click(); + expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy(); + }); + +
    + * + * @element INPUT + * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy, + * then special attribute "readonly" will be set on the element + */ + + +/** + * @ngdoc directive + * @name ngSelected + * @restrict A + * @priority 100 + * + * @description + * + * Sets the `selected` attribute on the element, if the expression inside `ngSelected` is truthy. + * + * A special directive is necessary because we cannot use interpolation inside the `selected` + * attribute. See the {@link guide/interpolation interpolation guide} for more info. + * + * @example + + +
    + +
    + + it('should select Greetings!', function() { + expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy(); + element(by.model('selected')).click(); + expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy(); + }); + +
    + * + * @element OPTION + * @param {expression} ngSelected If the {@link guide/expression expression} is truthy, + * then special attribute "selected" will be set on the element + */ + +/** + * @ngdoc directive + * @name ngOpen + * @restrict A + * @priority 100 + * + * @description + * + * Sets the `open` attribute on the element, if the expression inside `ngOpen` is truthy. + * + * A special directive is necessary because we cannot use interpolation inside the `open` + * attribute. See the {@link guide/interpolation interpolation guide} for more info. + * + * @example + + +
    +
    + Show/Hide me +
    +
    + + it('should toggle open', function() { + expect(element(by.id('details')).getAttribute('open')).toBeFalsy(); + element(by.model('open')).click(); + expect(element(by.id('details')).getAttribute('open')).toBeTruthy(); + }); + +
    + * + * @element DETAILS + * @param {expression} ngOpen If the {@link guide/expression expression} is truthy, + * then special attribute "open" will be set on the element + */ + +var ngAttributeAliasDirectives = {}; + +// boolean attrs are evaluated +forEach(BOOLEAN_ATTR, function(propName, attrName) { + // binding to multiple is not supported + if (propName == "multiple") return; + + function defaultLinkFn(scope, element, attr) { + scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) { + attr.$set(attrName, !!value); + }); + } + + var normalized = directiveNormalize('ng-' + attrName); + var linkFn = defaultLinkFn; + + if (propName === 'checked') { + linkFn = function(scope, element, attr) { + // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input + if (attr.ngModel !== attr[normalized]) { + defaultLinkFn(scope, element, attr); + } + }; + } + + ngAttributeAliasDirectives[normalized] = function() { + return { + restrict: 'A', + priority: 100, + link: linkFn + }; + }; +}); + +// aliased input attrs are evaluated +forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) { + ngAttributeAliasDirectives[ngAttr] = function() { + return { + priority: 100, + link: function(scope, element, attr) { + //special case ngPattern when a literal regular expression value + //is used as the expression (this way we don't have to watch anything). + if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") { + var match = attr.ngPattern.match(REGEX_STRING_REGEXP); + if (match) { + attr.$set("ngPattern", new RegExp(match[1], match[2])); + return; + } + } + + scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) { + attr.$set(ngAttr, value); + }); + } + }; + }; +}); + +// ng-src, ng-srcset, ng-href are interpolated +forEach(['src', 'srcset', 'href'], function(attrName) { + var normalized = directiveNormalize('ng-' + attrName); + ngAttributeAliasDirectives[normalized] = function() { + return { + priority: 99, // it needs to run after the attributes are interpolated + link: function(scope, element, attr) { + var propName = attrName, + name = attrName; + + if (attrName === 'href' && + toString.call(element.prop('href')) === '[object SVGAnimatedString]') { + name = 'xlinkHref'; + attr.$attr[name] = 'xlink:href'; + propName = null; + } + + attr.$observe(normalized, function(value) { + if (!value) { + if (attrName === 'href') { + attr.$set(name, null); + } + return; + } + + attr.$set(name, value); + + // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist + // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need + // to set the property as well to achieve the desired effect. + // we use attr[attrName] value since $set can sanitize the url. + if (msie && propName) element.prop(propName, attr[name]); + }); + } + }; + }; +}); + +/* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true + */ +var nullFormCtrl = { + $addControl: noop, + $$renameControl: nullFormRenameControl, + $removeControl: noop, + $setValidity: noop, + $setDirty: noop, + $setPristine: noop, + $setSubmitted: noop +}, +SUBMITTED_CLASS = 'ng-submitted'; + +function nullFormRenameControl(control, name) { + control.$name = name; +} + +/** + * @ngdoc type + * @name form.FormController + * + * @property {boolean} $pristine True if user has not interacted with the form yet. + * @property {boolean} $dirty True if user has already interacted with the form. + * @property {boolean} $valid True if all of the containing forms and controls are valid. + * @property {boolean} $invalid True if at least one containing control or form is invalid. + * @property {boolean} $pending True if at least one containing control or form is pending. + * @property {boolean} $submitted True if user has submitted the form even if its invalid. + * + * @property {Object} $error Is an object hash, containing references to controls or + * forms with failing validators, where: + * + * - keys are validation tokens (error names), + * - values are arrays of controls or forms that have a failing validator for given error name. + * + * Built-in validation tokens: + * + * - `email` + * - `max` + * - `maxlength` + * - `min` + * - `minlength` + * - `number` + * - `pattern` + * - `required` + * - `url` + * - `date` + * - `datetimelocal` + * - `time` + * - `week` + * - `month` + * + * @description + * `FormController` keeps track of all its controls and nested forms as well as the state of them, + * such as being valid/invalid or dirty/pristine. + * + * Each {@link ng.directive:form form} directive creates an instance + * of `FormController`. + * + */ +//asks for $scope to fool the BC controller module +FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate']; +function FormController(element, attrs, $scope, $animate, $interpolate) { + var form = this, + controls = []; + + // init state + form.$error = {}; + form.$$success = {}; + form.$pending = undefined; + form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope); + form.$dirty = false; + form.$pristine = true; + form.$valid = true; + form.$invalid = false; + form.$submitted = false; + form.$$parentForm = nullFormCtrl; + + /** + * @ngdoc method + * @name form.FormController#$rollbackViewValue + * + * @description + * Rollback all form controls pending updates to the `$modelValue`. + * + * Updates may be pending by a debounced event or because the input is waiting for a some future + * event defined in `ng-model-options`. This method is typically needed by the reset button of + * a form that uses `ng-model-options` to pend updates. + */ + form.$rollbackViewValue = function() { + forEach(controls, function(control) { + control.$rollbackViewValue(); + }); + }; + + /** + * @ngdoc method + * @name form.FormController#$commitViewValue + * + * @description + * Commit all form controls pending updates to the `$modelValue`. + * + * Updates may be pending by a debounced event or because the input is waiting for a some future + * event defined in `ng-model-options`. This method is rarely needed as `NgModelController` + * usually handles calling this in response to input events. + */ + form.$commitViewValue = function() { + forEach(controls, function(control) { + control.$commitViewValue(); + }); + }; + + /** + * @ngdoc method + * @name form.FormController#$addControl + * @param {object} control control object, either a {@link form.FormController} or an + * {@link ngModel.NgModelController} + * + * @description + * Register a control with the form. Input elements using ngModelController do this automatically + * when they are linked. + * + * Note that the current state of the control will not be reflected on the new parent form. This + * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine` + * state. + * + * However, if the method is used programmatically, for example by adding dynamically created controls, + * or controls that have been previously removed without destroying their corresponding DOM element, + * it's the developers responsibility to make sure the current state propagates to the parent form. + * + * For example, if an input control is added that is already `$dirty` and has `$error` properties, + * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form. + */ + form.$addControl = function(control) { + // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored + // and not added to the scope. Now we throw an error. + assertNotHasOwnProperty(control.$name, 'input'); + controls.push(control); + + if (control.$name) { + form[control.$name] = control; + } + + control.$$parentForm = form; + }; + + // Private API: rename a form control + form.$$renameControl = function(control, newName) { + var oldName = control.$name; + + if (form[oldName] === control) { + delete form[oldName]; + } + form[newName] = control; + control.$name = newName; + }; + + /** + * @ngdoc method + * @name form.FormController#$removeControl + * @param {object} control control object, either a {@link form.FormController} or an + * {@link ngModel.NgModelController} + * + * @description + * Deregister a control from the form. + * + * Input elements using ngModelController do this automatically when they are destroyed. + * + * Note that only the removed control's validation state (`$errors`etc.) will be removed from the + * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be + * different from case to case. For example, removing the only `$dirty` control from a form may or + * may not mean that the form is still `$dirty`. + */ + form.$removeControl = function(control) { + if (control.$name && form[control.$name] === control) { + delete form[control.$name]; + } + forEach(form.$pending, function(value, name) { + form.$setValidity(name, null, control); + }); + forEach(form.$error, function(value, name) { + form.$setValidity(name, null, control); + }); + forEach(form.$$success, function(value, name) { + form.$setValidity(name, null, control); + }); + + arrayRemove(controls, control); + control.$$parentForm = nullFormCtrl; + }; + + + /** + * @ngdoc method + * @name form.FormController#$setValidity + * + * @description + * Sets the validity of a form control. + * + * This method will also propagate to parent forms. + */ + addSetValidityMethod({ + ctrl: this, + $element: element, + set: function(object, property, controller) { + var list = object[property]; + if (!list) { + object[property] = [controller]; + } else { + var index = list.indexOf(controller); + if (index === -1) { + list.push(controller); + } + } + }, + unset: function(object, property, controller) { + var list = object[property]; + if (!list) { + return; + } + arrayRemove(list, controller); + if (list.length === 0) { + delete object[property]; + } + }, + $animate: $animate + }); + + /** + * @ngdoc method + * @name form.FormController#$setDirty + * + * @description + * Sets the form to a dirty state. + * + * This method can be called to add the 'ng-dirty' class and set the form to a dirty + * state (ng-dirty class). This method will also propagate to parent forms. + */ + form.$setDirty = function() { + $animate.removeClass(element, PRISTINE_CLASS); + $animate.addClass(element, DIRTY_CLASS); + form.$dirty = true; + form.$pristine = false; + form.$$parentForm.$setDirty(); + }; + + /** + * @ngdoc method + * @name form.FormController#$setPristine + * + * @description + * Sets the form to its pristine state. + * + * This method can be called to remove the 'ng-dirty' class and set the form to its pristine + * state (ng-pristine class). This method will also propagate to all the controls contained + * in this form. + * + * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after + * saving or resetting it. + */ + form.$setPristine = function() { + $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS); + form.$dirty = false; + form.$pristine = true; + form.$submitted = false; + forEach(controls, function(control) { + control.$setPristine(); + }); + }; + + /** + * @ngdoc method + * @name form.FormController#$setUntouched + * + * @description + * Sets the form to its untouched state. + * + * This method can be called to remove the 'ng-touched' class and set the form controls to their + * untouched state (ng-untouched class). + * + * Setting a form controls back to their untouched state is often useful when setting the form + * back to its pristine state. + */ + form.$setUntouched = function() { + forEach(controls, function(control) { + control.$setUntouched(); + }); + }; + + /** + * @ngdoc method + * @name form.FormController#$setSubmitted + * + * @description + * Sets the form to its submitted state. + */ + form.$setSubmitted = function() { + $animate.addClass(element, SUBMITTED_CLASS); + form.$submitted = true; + form.$$parentForm.$setSubmitted(); + }; +} + +/** + * @ngdoc directive + * @name ngForm + * @restrict EAC + * + * @description + * Nestable alias of {@link ng.directive:form `form`} directive. HTML + * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a + * sub-group of controls needs to be determined. + * + * Note: the purpose of `ngForm` is to group controls, + * but not to be a replacement for the `
    ` tag with all of its capabilities + * (e.g. posting to the server, ...). + * + * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into + * related scope, under this name. + * + */ + + /** + * @ngdoc directive + * @name form + * @restrict E + * + * @description + * Directive that instantiates + * {@link form.FormController FormController}. + * + * If the `name` attribute is specified, the form controller is published onto the current scope under + * this name. + * + * # Alias: {@link ng.directive:ngForm `ngForm`} + * + * In Angular, forms can be nested. This means that the outer form is valid when all of the child + * forms are valid as well. However, browsers do not allow nesting of `` elements, so + * Angular provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to + * `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group + * of controls needs to be determined. + * + * # CSS classes + * - `ng-valid` is set if the form is valid. + * - `ng-invalid` is set if the form is invalid. + * - `ng-pending` is set if the form is pending. + * - `ng-pristine` is set if the form is pristine. + * - `ng-dirty` is set if the form is dirty. + * - `ng-submitted` is set if the form was submitted. + * + * Keep in mind that ngAnimate can detect each of these classes when added and removed. + * + * + * # Submitting a form and preventing the default action + * + * Since the role of forms in client-side Angular applications is different than in classical + * roundtrip apps, it is desirable for the browser not to translate the form submission into a full + * page reload that sends the data to the server. Instead some javascript logic should be triggered + * to handle the form submission in an application-specific way. + * + * For this reason, Angular prevents the default action (form submission to the server) unless the + * `` element has an `action` attribute specified. + * + * You can use one of the following two ways to specify what javascript method should be called when + * a form is submitted: + * + * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element + * - {@link ng.directive:ngClick ngClick} directive on the first + * button or input field of type submit (input[type=submit]) + * + * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit} + * or {@link ng.directive:ngClick ngClick} directives. + * This is because of the following form submission rules in the HTML specification: + * + * - If a form has only one input field then hitting enter in this field triggers form submit + * (`ngSubmit`) + * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter + * doesn't trigger submit + * - if a form has one or more input fields and one or more buttons or input[type=submit] then + * hitting enter in any of the input fields will trigger the click handler on the *first* button or + * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`) + * + * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is + * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit` + * to have access to the updated model. + * + * ## Animation Hooks + * + * Animations in ngForm are triggered when any of the associated CSS classes are added and removed. + * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any + * other validations that are performed within the form. Animations in ngForm are similar to how + * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well + * as JS animations. + * + * The following example shows a simple way to utilize CSS transitions to style a form element + * that has been rendered as invalid after it has been validated: + * + *
    + * //be sure to include ngAnimate as a module to hook into more
    + * //advanced animations
    + * .my-form {
    + *   transition:0.5s linear all;
    + *   background: white;
    + * }
    + * .my-form.ng-invalid {
    + *   background: red;
    + *   color:white;
    + * }
    + * 
    + * + * @example + + + + + + userType: + Required!
    + userType = {{userType}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    + +
    + + it('should initialize to model', function() { + var userType = element(by.binding('userType')); + var valid = element(by.binding('myForm.input.$valid')); + + expect(userType.getText()).toContain('guest'); + expect(valid.getText()).toContain('true'); + }); + + it('should be invalid if empty', function() { + var userType = element(by.binding('userType')); + var valid = element(by.binding('myForm.input.$valid')); + var userInput = element(by.model('userType')); + + userInput.clear(); + userInput.sendKeys(''); + + expect(userType.getText()).toEqual('userType ='); + expect(valid.getText()).toContain('false'); + }); + +
    + * + * @param {string=} name Name of the form. If specified, the form controller will be published into + * related scope, under this name. + */ +var formDirectiveFactory = function(isNgForm) { + return ['$timeout', '$parse', function($timeout, $parse) { + var formDirective = { + name: 'form', + restrict: isNgForm ? 'EAC' : 'E', + require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form + controller: FormController, + compile: function ngFormCompile(formElement, attr) { + // Setup initial state of the control + formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS); + + var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false); + + return { + pre: function ngFormPreLink(scope, formElement, attr, ctrls) { + var controller = ctrls[0]; + + // if `action` attr is not present on the form, prevent the default action (submission) + if (!('action' in attr)) { + // we can't use jq events because if a form is destroyed during submission the default + // action is not prevented. see #1238 + // + // IE 9 is not affected because it doesn't fire a submit event and try to do a full + // page reload if the form was destroyed by submission of the form via a click handler + // on a button in the form. Looks like an IE9 specific bug. + var handleFormSubmission = function(event) { + scope.$apply(function() { + controller.$commitViewValue(); + controller.$setSubmitted(); + }); + + event.preventDefault(); + }; + + addEventListenerFn(formElement[0], 'submit', handleFormSubmission); + + // unregister the preventDefault listener so that we don't not leak memory but in a + // way that will achieve the prevention of the default action. + formElement.on('$destroy', function() { + $timeout(function() { + removeEventListenerFn(formElement[0], 'submit', handleFormSubmission); + }, 0, false); + }); + } + + var parentFormCtrl = ctrls[1] || controller.$$parentForm; + parentFormCtrl.$addControl(controller); + + var setter = nameAttr ? getSetter(controller.$name) : noop; + + if (nameAttr) { + setter(scope, controller); + attr.$observe(nameAttr, function(newValue) { + if (controller.$name === newValue) return; + setter(scope, undefined); + controller.$$parentForm.$$renameControl(controller, newValue); + setter = getSetter(controller.$name); + setter(scope, controller); + }); + } + formElement.on('$destroy', function() { + controller.$$parentForm.$removeControl(controller); + setter(scope, undefined); + extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards + }); + } + }; + } + }; + + return formDirective; + + function getSetter(expression) { + if (expression === '') { + //create an assignable expression, so forms with an empty name can be renamed later + return $parse('this[""]').assign; + } + return $parse(expression).assign || noop; + } + }]; +}; + +var formDirective = formDirectiveFactory(); +var ngFormDirective = formDirectiveFactory(true); + +/* global VALID_CLASS: false, + INVALID_CLASS: false, + PRISTINE_CLASS: false, + DIRTY_CLASS: false, + UNTOUCHED_CLASS: false, + TOUCHED_CLASS: false, + ngModelMinErr: false, +*/ + +// Regex code was initially obtained from SO prior to modification: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231 +var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z)$/; +// See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987) +// Note: We are being more lenient, because browsers are too. +// 1. Scheme +// 2. Slashes +// 3. Username +// 4. Password +// 5. Hostname +// 6. Port +// 7. Path +// 8. Query +// 9. Fragment +// 1111111111111111 222 333333 44444 555555555555555555555555 666 77777777 8888888 999 +var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i; +var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i; +var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/; +var DATE_REGEXP = /^(\d{4,})-(\d{2})-(\d{2})$/; +var DATETIMELOCAL_REGEXP = /^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/; +var WEEK_REGEXP = /^(\d{4,})-W(\d\d)$/; +var MONTH_REGEXP = /^(\d{4,})-(\d\d)$/; +var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/; + +var PARTIAL_VALIDATION_EVENTS = 'keydown wheel mousedown'; +var PARTIAL_VALIDATION_TYPES = createMap(); +forEach('date,datetime-local,month,time,week'.split(','), function(type) { + PARTIAL_VALIDATION_TYPES[type] = true; +}); + +var inputType = { + + /** + * @ngdoc input + * @name input[text] + * + * @description + * Standard HTML text input with angular data binding, inherited by most of the `input` elements. + * + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Adds `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of + * any length. + * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string + * that contains the regular expression body that will be converted to a regular expression + * as in the ngPattern directive. + * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue} + * does not match a RegExp found by evaluating the Angular expression given in the attribute value. + * If the expression evaluates to a RegExp object, then this is used directly. + * If the expression evaluates to a string, then it will be converted to a RegExp + * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to + * `new RegExp('^abc$')`.
    + * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to + * start at the index of the last search's match, thus not taking the whole input value into + * account. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. + * This parameter is ignored for input[type=password] controls, which will never trim the + * input. + * + * @example + + + +
    + +
    + + Required! + + Single word only! +
    + text = {{example.text}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    +
    +
    + + var text = element(by.binding('example.text')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('example.text')); + + it('should initialize to model', function() { + expect(text.getText()).toContain('guest'); + expect(valid.getText()).toContain('true'); + }); + + it('should be invalid if empty', function() { + input.clear(); + input.sendKeys(''); + + expect(text.getText()).toEqual('text ='); + expect(valid.getText()).toContain('false'); + }); + + it('should be invalid if multi word', function() { + input.clear(); + input.sendKeys('hello world'); + + expect(valid.getText()).toContain('false'); + }); + +
    + */ + 'text': textInputType, + + /** + * @ngdoc input + * @name input[date] + * + * @description + * Input with date validation and transformation. In browsers that do not yet support + * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601 + * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many + * modern browsers do not yet support this input type, it is important to provide cues to users on the + * expected input format via a placeholder or label. + * + * The model must always be a Date object, otherwise Angular will throw an error. + * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. + * + * The timezone to be used to read/write the `Date` instance in the model can be defined using + * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a + * valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute + * (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5 + * constraint validation. + * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be + * a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute + * (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5 + * constraint validation. + * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string + * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute. + * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string + * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + + +
    + + Required! + + Not a valid date! +
    + value = {{example.value | date: "yyyy-MM-dd"}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    +
    +
    + + var value = element(by.binding('example.value | date: "yyyy-MM-dd"')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('example.value')); + + // currently protractor/webdriver does not support + // sending keys to all known HTML5 input controls + // for various browsers (see https://github.com/angular/protractor/issues/562). + function setInput(val) { + // set the value of the element and force validation. + var scr = "var ipt = document.getElementById('exampleInput'); " + + "ipt.value = '" + val + "';" + + "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; + browser.executeScript(scr); + } + + it('should initialize to model', function() { + expect(value.getText()).toContain('2013-10-22'); + expect(valid.getText()).toContain('myForm.input.$valid = true'); + }); + + it('should be invalid if empty', function() { + setInput(''); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + + it('should be invalid if over max', function() { + setInput('2015-01-01'); + expect(value.getText()).toContain(''); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + +
    + */ + 'date': createDateInputType('date', DATE_REGEXP, + createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']), + 'yyyy-MM-dd'), + + /** + * @ngdoc input + * @name input[datetime-local] + * + * @description + * Input with datetime validation and transformation. In browsers that do not yet support + * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 + * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`. + * + * The model must always be a Date object, otherwise Angular will throw an error. + * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. + * + * The timezone to be used to read/write the `Date` instance in the model can be defined using + * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. + * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation + * inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`). + * Note that `min` will also add native HTML5 constraint validation. + * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. + * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation + * inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`). + * Note that `max` will also add native HTML5 constraint validation. + * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string + * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute. + * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string + * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + + +
    + + Required! + + Not a valid date! +
    + value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    +
    +
    + + var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('example.value')); + + // currently protractor/webdriver does not support + // sending keys to all known HTML5 input controls + // for various browsers (https://github.com/angular/protractor/issues/562). + function setInput(val) { + // set the value of the element and force validation. + var scr = "var ipt = document.getElementById('exampleInput'); " + + "ipt.value = '" + val + "';" + + "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; + browser.executeScript(scr); + } + + it('should initialize to model', function() { + expect(value.getText()).toContain('2010-12-28T14:57:00'); + expect(valid.getText()).toContain('myForm.input.$valid = true'); + }); + + it('should be invalid if empty', function() { + setInput(''); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + + it('should be invalid if over max', function() { + setInput('2015-01-01T23:59:00'); + expect(value.getText()).toContain(''); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + +
    + */ + 'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP, + createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']), + 'yyyy-MM-ddTHH:mm:ss.sss'), + + /** + * @ngdoc input + * @name input[time] + * + * @description + * Input with time validation and transformation. In browsers that do not yet support + * the HTML5 time input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 + * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a + * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`. + * + * The model must always be a Date object, otherwise Angular will throw an error. + * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. + * + * The timezone to be used to read/write the `Date` instance in the model can be defined using + * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. + * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this + * attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add + * native HTML5 constraint validation. + * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. + * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this + * attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add + * native HTML5 constraint validation. + * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the + * `ngMin` expression evaluates to. Note that it does not set the `min` attribute. + * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the + * `ngMax` expression evaluates to. Note that it does not set the `max` attribute. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + + +
    + + Required! + + Not a valid date! +
    + value = {{example.value | date: "HH:mm:ss"}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    +
    +
    + + var value = element(by.binding('example.value | date: "HH:mm:ss"')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('example.value')); + + // currently protractor/webdriver does not support + // sending keys to all known HTML5 input controls + // for various browsers (https://github.com/angular/protractor/issues/562). + function setInput(val) { + // set the value of the element and force validation. + var scr = "var ipt = document.getElementById('exampleInput'); " + + "ipt.value = '" + val + "';" + + "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; + browser.executeScript(scr); + } + + it('should initialize to model', function() { + expect(value.getText()).toContain('14:57:00'); + expect(valid.getText()).toContain('myForm.input.$valid = true'); + }); + + it('should be invalid if empty', function() { + setInput(''); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + + it('should be invalid if over max', function() { + setInput('23:59:00'); + expect(value.getText()).toContain(''); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + +
    + */ + 'time': createDateInputType('time', TIME_REGEXP, + createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']), + 'HH:mm:ss.sss'), + + /** + * @ngdoc input + * @name input[week] + * + * @description + * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support + * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 + * week format (yyyy-W##), for example: `2013-W02`. + * + * The model must always be a Date object, otherwise Angular will throw an error. + * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. + * + * The timezone to be used to read/write the `Date` instance in the model can be defined using + * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. + * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this + * attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add + * native HTML5 constraint validation. + * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. + * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this + * attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add + * native HTML5 constraint validation. + * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string + * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute. + * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string + * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + +
    + + Required! + + Not a valid date! +
    + value = {{example.value | date: "yyyy-Www"}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    +
    +
    + + var value = element(by.binding('example.value | date: "yyyy-Www"')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('example.value')); + + // currently protractor/webdriver does not support + // sending keys to all known HTML5 input controls + // for various browsers (https://github.com/angular/protractor/issues/562). + function setInput(val) { + // set the value of the element and force validation. + var scr = "var ipt = document.getElementById('exampleInput'); " + + "ipt.value = '" + val + "';" + + "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; + browser.executeScript(scr); + } + + it('should initialize to model', function() { + expect(value.getText()).toContain('2013-W01'); + expect(valid.getText()).toContain('myForm.input.$valid = true'); + }); + + it('should be invalid if empty', function() { + setInput(''); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + + it('should be invalid if over max', function() { + setInput('2015-W01'); + expect(value.getText()).toContain(''); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + +
    + */ + 'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'), + + /** + * @ngdoc input + * @name input[month] + * + * @description + * Input with month validation and transformation. In browsers that do not yet support + * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 + * month format (yyyy-MM), for example: `2009-01`. + * + * The model must always be a Date object, otherwise Angular will throw an error. + * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. + * If the model is not set to the first of the month, the next view to model update will set it + * to the first of the month. + * + * The timezone to be used to read/write the `Date` instance in the model can be defined using + * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. + * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this + * attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add + * native HTML5 constraint validation. + * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. + * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this + * attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add + * native HTML5 constraint validation. + * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string + * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute. + * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string + * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute. + + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + + +
    + + Required! + + Not a valid month! +
    + value = {{example.value | date: "yyyy-MM"}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    +
    +
    + + var value = element(by.binding('example.value | date: "yyyy-MM"')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('example.value')); + + // currently protractor/webdriver does not support + // sending keys to all known HTML5 input controls + // for various browsers (https://github.com/angular/protractor/issues/562). + function setInput(val) { + // set the value of the element and force validation. + var scr = "var ipt = document.getElementById('exampleInput'); " + + "ipt.value = '" + val + "';" + + "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; + browser.executeScript(scr); + } + + it('should initialize to model', function() { + expect(value.getText()).toContain('2013-10'); + expect(valid.getText()).toContain('myForm.input.$valid = true'); + }); + + it('should be invalid if empty', function() { + setInput(''); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + + it('should be invalid if over max', function() { + setInput('2015-01'); + expect(value.getText()).toContain(''); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + +
    + */ + 'month': createDateInputType('month', MONTH_REGEXP, + createDateParser(MONTH_REGEXP, ['yyyy', 'MM']), + 'yyyy-MM'), + + /** + * @ngdoc input + * @name input[number] + * + * @description + * Text input with number validation and transformation. Sets the `number` validation + * error if not a valid number. + * + *
    + * The model must always be of type `number` otherwise Angular will throw an error. + * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt} + * error docs for more information and an example of how to convert your model if necessary. + *
    + * + * ## Issues with HTML5 constraint validation + * + * In browsers that follow the + * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29), + * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}. + * If a non-number is entered in the input, the browser will report the value as an empty string, + * which means the view / model values in `ngModel` and subsequently the scope value + * will also be an empty string. + * + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. + * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of + * any length. + * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string + * that contains the regular expression body that will be converted to a regular expression + * as in the ngPattern directive. + * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue} + * does not match a RegExp found by evaluating the Angular expression given in the attribute value. + * If the expression evaluates to a RegExp object, then this is used directly. + * If the expression evaluates to a string, then it will be converted to a RegExp + * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to + * `new RegExp('^abc$')`.
    + * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to + * start at the index of the last search's match, thus not taking the whole input value into + * account. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + +
    + + Required! + + Not valid number! +
    + value = {{example.value}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    +
    +
    + + var value = element(by.binding('example.value')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('example.value')); + + it('should initialize to model', function() { + expect(value.getText()).toContain('12'); + expect(valid.getText()).toContain('true'); + }); + + it('should be invalid if empty', function() { + input.clear(); + input.sendKeys(''); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('false'); + }); + + it('should be invalid if over max', function() { + input.clear(); + input.sendKeys('123'); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('false'); + }); + +
    + */ + 'number': numberInputType, + + + /** + * @ngdoc input + * @name input[url] + * + * @description + * Text input with URL validation. Sets the `url` validation error key if the content is not a + * valid URL. + * + *
    + * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex + * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify + * the built-in validators (see the {@link guide/forms Forms guide}) + *
    + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of + * any length. + * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string + * that contains the regular expression body that will be converted to a regular expression + * as in the ngPattern directive. + * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue} + * does not match a RegExp found by evaluating the Angular expression given in the attribute value. + * If the expression evaluates to a RegExp object, then this is used directly. + * If the expression evaluates to a string, then it will be converted to a RegExp + * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to + * `new RegExp('^abc$')`.
    + * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to + * start at the index of the last search's match, thus not taking the whole input value into + * account. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    +
    + + var text = element(by.binding('url.text')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('url.text')); + + it('should initialize to model', function() { + expect(text.getText()).toContain('http://google.com'); + expect(valid.getText()).toContain('true'); + }); + + it('should be invalid if empty', function() { + input.clear(); + input.sendKeys(''); + + expect(text.getText()).toEqual('text ='); + expect(valid.getText()).toContain('false'); + }); + + it('should be invalid if not url', function() { + input.clear(); + input.sendKeys('box'); + + expect(valid.getText()).toContain('false'); + }); + +
    + */ + 'url': urlInputType, + + + /** + * @ngdoc input + * @name input[email] + * + * @description + * Text input with email validation. Sets the `email` validation error key if not a valid email + * address. + * + *
    + * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex + * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can + * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide}) + *
    + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of + * any length. + * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string + * that contains the regular expression body that will be converted to a regular expression + * as in the ngPattern directive. + * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue} + * does not match a RegExp found by evaluating the Angular expression given in the attribute value. + * If the expression evaluates to a RegExp object, then this is used directly. + * If the expression evaluates to a string, then it will be converted to a RegExp + * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to + * `new RegExp('^abc$')`.
    + * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to + * start at the index of the last search's match, thus not taking the whole input value into + * account. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + +
    + + Required! + + Not valid email! +
    + text = {{email.text}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    + myForm.$error.email = {{!!myForm.$error.email}}
    +
    +
    + + var text = element(by.binding('email.text')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('email.text')); + + it('should initialize to model', function() { + expect(text.getText()).toContain('me@example.com'); + expect(valid.getText()).toContain('true'); + }); + + it('should be invalid if empty', function() { + input.clear(); + input.sendKeys(''); + expect(text.getText()).toEqual('text ='); + expect(valid.getText()).toContain('false'); + }); + + it('should be invalid if not email', function() { + input.clear(); + input.sendKeys('xxx'); + + expect(valid.getText()).toContain('false'); + }); + +
    + */ + 'email': emailInputType, + + + /** + * @ngdoc input + * @name input[radio] + * + * @description + * HTML radio button. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string} value The value to which the `ngModel` expression should be set when selected. + * Note that `value` only supports `string` values, i.e. the scope model needs to be a string, + * too. Use `ngValue` if you need complex models (`number`, `object`, ...). + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio + * is selected. Should be used instead of the `value` attribute if you need + * a non-string `ngModel` (`boolean`, `array`, ...). + * + * @example + + + +
    +
    +
    +
    + color = {{color.name | json}}
    +
    + Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`. +
    + + it('should change state', function() { + var color = element(by.binding('color.name')); + + expect(color.getText()).toContain('blue'); + + element.all(by.model('color.name')).get(0).click(); + + expect(color.getText()).toContain('red'); + }); + +
    + */ + 'radio': radioInputType, + + + /** + * @ngdoc input + * @name input[checkbox] + * + * @description + * HTML checkbox. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {expression=} ngTrueValue The value to which the expression should be set when selected. + * @param {expression=} ngFalseValue The value to which the expression should be set when not selected. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    +
    +
    + value1 = {{checkboxModel.value1}}
    + value2 = {{checkboxModel.value2}}
    +
    +
    + + it('should change state', function() { + var value1 = element(by.binding('checkboxModel.value1')); + var value2 = element(by.binding('checkboxModel.value2')); + + expect(value1.getText()).toContain('true'); + expect(value2.getText()).toContain('YES'); + + element(by.model('checkboxModel.value1')).click(); + element(by.model('checkboxModel.value2')).click(); + + expect(value1.getText()).toContain('false'); + expect(value2.getText()).toContain('NO'); + }); + +
    + */ + 'checkbox': checkboxInputType, + + 'hidden': noop, + 'button': noop, + 'submit': noop, + 'reset': noop, + 'file': noop +}; + +function stringBasedInputType(ctrl) { + ctrl.$formatters.push(function(value) { + return ctrl.$isEmpty(value) ? value : value.toString(); + }); +} + +function textInputType(scope, element, attr, ctrl, $sniffer, $browser) { + baseInputType(scope, element, attr, ctrl, $sniffer, $browser); + stringBasedInputType(ctrl); +} + +function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) { + var type = lowercase(element[0].type); + + // In composition mode, users are still inputing intermediate text buffer, + // hold the listener until composition is done. + // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent + if (!$sniffer.android) { + var composing = false; + + element.on('compositionstart', function() { + composing = true; + }); + + element.on('compositionend', function() { + composing = false; + listener(); + }); + } + + var timeout; + + var listener = function(ev) { + if (timeout) { + $browser.defer.cancel(timeout); + timeout = null; + } + if (composing) return; + var value = element.val(), + event = ev && ev.type; + + // By default we will trim the value + // If the attribute ng-trim exists we will avoid trimming + // If input type is 'password', the value is never trimmed + if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) { + value = trim(value); + } + + // If a control is suffering from bad input (due to native validators), browsers discard its + // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the + // control's value is the same empty value twice in a row. + if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) { + ctrl.$setViewValue(value, event); + } + }; + + // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the + // input event on backspace, delete or cut + if ($sniffer.hasEvent('input')) { + element.on('input', listener); + } else { + var deferListener = function(ev, input, origValue) { + if (!timeout) { + timeout = $browser.defer(function() { + timeout = null; + if (!input || input.value !== origValue) { + listener(ev); + } + }); + } + }; + + element.on('keydown', function(event) { + var key = event.keyCode; + + // ignore + // command modifiers arrows + if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return; + + deferListener(event, this, this.value); + }); + + // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it + if ($sniffer.hasEvent('paste')) { + element.on('paste cut', deferListener); + } + } + + // if user paste into input using mouse on older browser + // or form autocomplete on newer browser, we need "change" event to catch it + element.on('change', listener); + + // Some native input types (date-family) have the ability to change validity without + // firing any input/change events. + // For these event types, when native validators are present and the browser supports the type, + // check for validity changes on various DOM events. + if (PARTIAL_VALIDATION_TYPES[type] && ctrl.$$hasNativeValidators && type === attr.type) { + element.on(PARTIAL_VALIDATION_EVENTS, function(ev) { + if (!timeout) { + var validity = this[VALIDITY_STATE_PROPERTY]; + var origBadInput = validity.badInput; + var origTypeMismatch = validity.typeMismatch; + timeout = $browser.defer(function() { + timeout = null; + if (validity.badInput !== origBadInput || validity.typeMismatch !== origTypeMismatch) { + listener(ev); + } + }); + } + }); + } + + ctrl.$render = function() { + // Workaround for Firefox validation #12102. + var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue; + if (element.val() !== value) { + element.val(value); + } + }; +} + +function weekParser(isoWeek, existingDate) { + if (isDate(isoWeek)) { + return isoWeek; + } + + if (isString(isoWeek)) { + WEEK_REGEXP.lastIndex = 0; + var parts = WEEK_REGEXP.exec(isoWeek); + if (parts) { + var year = +parts[1], + week = +parts[2], + hours = 0, + minutes = 0, + seconds = 0, + milliseconds = 0, + firstThurs = getFirstThursdayOfYear(year), + addDays = (week - 1) * 7; + + if (existingDate) { + hours = existingDate.getHours(); + minutes = existingDate.getMinutes(); + seconds = existingDate.getSeconds(); + milliseconds = existingDate.getMilliseconds(); + } + + return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds); + } + } + + return NaN; +} + +function createDateParser(regexp, mapping) { + return function(iso, date) { + var parts, map; + + if (isDate(iso)) { + return iso; + } + + if (isString(iso)) { + // When a date is JSON'ified to wraps itself inside of an extra + // set of double quotes. This makes the date parsing code unable + // to match the date string and parse it as a date. + if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') { + iso = iso.substring(1, iso.length - 1); + } + if (ISO_DATE_REGEXP.test(iso)) { + return new Date(iso); + } + regexp.lastIndex = 0; + parts = regexp.exec(iso); + + if (parts) { + parts.shift(); + if (date) { + map = { + yyyy: date.getFullYear(), + MM: date.getMonth() + 1, + dd: date.getDate(), + HH: date.getHours(), + mm: date.getMinutes(), + ss: date.getSeconds(), + sss: date.getMilliseconds() / 1000 + }; + } else { + map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 }; + } + + forEach(parts, function(part, index) { + if (index < mapping.length) { + map[mapping[index]] = +part; + } + }); + return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0); + } + } + + return NaN; + }; +} + +function createDateInputType(type, regexp, parseDate, format) { + return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) { + badInputChecker(scope, element, attr, ctrl); + baseInputType(scope, element, attr, ctrl, $sniffer, $browser); + var timezone = ctrl && ctrl.$options && ctrl.$options.timezone; + var previousDate; + + ctrl.$$parserName = type; + ctrl.$parsers.push(function(value) { + if (ctrl.$isEmpty(value)) return null; + if (regexp.test(value)) { + // Note: We cannot read ctrl.$modelValue, as there might be a different + // parser/formatter in the processing chain so that the model + // contains some different data format! + var parsedDate = parseDate(value, previousDate); + if (timezone) { + parsedDate = convertTimezoneToLocal(parsedDate, timezone); + } + return parsedDate; + } + return undefined; + }); + + ctrl.$formatters.push(function(value) { + if (value && !isDate(value)) { + throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value); + } + if (isValidDate(value)) { + previousDate = value; + if (previousDate && timezone) { + previousDate = convertTimezoneToLocal(previousDate, timezone, true); + } + return $filter('date')(value, format, timezone); + } else { + previousDate = null; + return ''; + } + }); + + if (isDefined(attr.min) || attr.ngMin) { + var minVal; + ctrl.$validators.min = function(value) { + return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal; + }; + attr.$observe('min', function(val) { + minVal = parseObservedDateValue(val); + ctrl.$validate(); + }); + } + + if (isDefined(attr.max) || attr.ngMax) { + var maxVal; + ctrl.$validators.max = function(value) { + return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal; + }; + attr.$observe('max', function(val) { + maxVal = parseObservedDateValue(val); + ctrl.$validate(); + }); + } + + function isValidDate(value) { + // Invalid Date: getTime() returns NaN + return value && !(value.getTime && value.getTime() !== value.getTime()); + } + + function parseObservedDateValue(val) { + return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val; + } + }; +} + +function badInputChecker(scope, element, attr, ctrl) { + var node = element[0]; + var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity); + if (nativeValidation) { + ctrl.$parsers.push(function(value) { + var validity = element.prop(VALIDITY_STATE_PROPERTY) || {}; + return validity.badInput || validity.typeMismatch ? undefined : value; + }); + } +} + +function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) { + badInputChecker(scope, element, attr, ctrl); + baseInputType(scope, element, attr, ctrl, $sniffer, $browser); + + ctrl.$$parserName = 'number'; + ctrl.$parsers.push(function(value) { + if (ctrl.$isEmpty(value)) return null; + if (NUMBER_REGEXP.test(value)) return parseFloat(value); + return undefined; + }); + + ctrl.$formatters.push(function(value) { + if (!ctrl.$isEmpty(value)) { + if (!isNumber(value)) { + throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value); + } + value = value.toString(); + } + return value; + }); + + if (isDefined(attr.min) || attr.ngMin) { + var minVal; + ctrl.$validators.min = function(value) { + return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal; + }; + + attr.$observe('min', function(val) { + if (isDefined(val) && !isNumber(val)) { + val = parseFloat(val, 10); + } + minVal = isNumber(val) && !isNaN(val) ? val : undefined; + // TODO(matsko): implement validateLater to reduce number of validations + ctrl.$validate(); + }); + } + + if (isDefined(attr.max) || attr.ngMax) { + var maxVal; + ctrl.$validators.max = function(value) { + return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal; + }; + + attr.$observe('max', function(val) { + if (isDefined(val) && !isNumber(val)) { + val = parseFloat(val, 10); + } + maxVal = isNumber(val) && !isNaN(val) ? val : undefined; + // TODO(matsko): implement validateLater to reduce number of validations + ctrl.$validate(); + }); + } +} + +function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) { + // Note: no badInputChecker here by purpose as `url` is only a validation + // in browsers, i.e. we can always read out input.value even if it is not valid! + baseInputType(scope, element, attr, ctrl, $sniffer, $browser); + stringBasedInputType(ctrl); + + ctrl.$$parserName = 'url'; + ctrl.$validators.url = function(modelValue, viewValue) { + var value = modelValue || viewValue; + return ctrl.$isEmpty(value) || URL_REGEXP.test(value); + }; +} + +function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) { + // Note: no badInputChecker here by purpose as `url` is only a validation + // in browsers, i.e. we can always read out input.value even if it is not valid! + baseInputType(scope, element, attr, ctrl, $sniffer, $browser); + stringBasedInputType(ctrl); + + ctrl.$$parserName = 'email'; + ctrl.$validators.email = function(modelValue, viewValue) { + var value = modelValue || viewValue; + return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value); + }; +} + +function radioInputType(scope, element, attr, ctrl) { + // make the name unique, if not defined + if (isUndefined(attr.name)) { + element.attr('name', nextUid()); + } + + var listener = function(ev) { + if (element[0].checked) { + ctrl.$setViewValue(attr.value, ev && ev.type); + } + }; + + element.on('click', listener); + + ctrl.$render = function() { + var value = attr.value; + element[0].checked = (value == ctrl.$viewValue); + }; + + attr.$observe('value', ctrl.$render); +} + +function parseConstantExpr($parse, context, name, expression, fallback) { + var parseFn; + if (isDefined(expression)) { + parseFn = $parse(expression); + if (!parseFn.constant) { + throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' + + '`{1}`.', name, expression); + } + return parseFn(context); + } + return fallback; +} + +function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) { + var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true); + var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false); + + var listener = function(ev) { + ctrl.$setViewValue(element[0].checked, ev && ev.type); + }; + + element.on('click', listener); + + ctrl.$render = function() { + element[0].checked = ctrl.$viewValue; + }; + + // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false` + // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert + // it to a boolean. + ctrl.$isEmpty = function(value) { + return value === false; + }; + + ctrl.$formatters.push(function(value) { + return equals(value, trueValue); + }); + + ctrl.$parsers.push(function(value) { + return value ? trueValue : falseValue; + }); +} + + +/** + * @ngdoc directive + * @name textarea + * @restrict E + * + * @description + * HTML textarea element control with angular data-binding. The data-binding and validation + * properties of this element are exactly the same as those of the + * {@link ng.directive:input input element}. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any + * length. + * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue} + * does not match a RegExp found by evaluating the Angular expression given in the attribute value. + * If the expression evaluates to a RegExp object, then this is used directly. + * If the expression evaluates to a string, then it will be converted to a RegExp + * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to + * `new RegExp('^abc$')`.
    + * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to + * start at the index of the last search's match, thus not taking the whole input value into + * account. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. + */ + + +/** + * @ngdoc directive + * @name input + * @restrict E + * + * @description + * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding, + * input state control, and validation. + * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers. + * + *
    + * **Note:** Not every feature offered is available for all input types. + * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`. + *
    + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {boolean=} ngRequired Sets `required` attribute if set to true + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any + * length. + * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue} + * value does not match a RegExp found by evaluating the Angular expression given in the attribute value. + * If the expression evaluates to a RegExp object, then this is used directly. + * If the expression evaluates to a string, then it will be converted to a RegExp + * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to + * `new RegExp('^abc$')`.
    + * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to + * start at the index of the last search's match, thus not taking the whole input value into + * account. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. + * This parameter is ignored for input[type=password] controls, which will never trim the + * input. + * + * @example + + + +
    +
    + +
    + + Required! +
    + +
    + + Too short! + + Too long! +
    +
    +
    + user = {{user}}
    + myForm.userName.$valid = {{myForm.userName.$valid}}
    + myForm.userName.$error = {{myForm.userName.$error}}
    + myForm.lastName.$valid = {{myForm.lastName.$valid}}
    + myForm.lastName.$error = {{myForm.lastName.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    + myForm.$error.minlength = {{!!myForm.$error.minlength}}
    + myForm.$error.maxlength = {{!!myForm.$error.maxlength}}
    +
    +
    + + var user = element(by.exactBinding('user')); + var userNameValid = element(by.binding('myForm.userName.$valid')); + var lastNameValid = element(by.binding('myForm.lastName.$valid')); + var lastNameError = element(by.binding('myForm.lastName.$error')); + var formValid = element(by.binding('myForm.$valid')); + var userNameInput = element(by.model('user.name')); + var userLastInput = element(by.model('user.last')); + + it('should initialize to model', function() { + expect(user.getText()).toContain('{"name":"guest","last":"visitor"}'); + expect(userNameValid.getText()).toContain('true'); + expect(formValid.getText()).toContain('true'); + }); + + it('should be invalid if empty when required', function() { + userNameInput.clear(); + userNameInput.sendKeys(''); + + expect(user.getText()).toContain('{"last":"visitor"}'); + expect(userNameValid.getText()).toContain('false'); + expect(formValid.getText()).toContain('false'); + }); + + it('should be valid if empty when min length is set', function() { + userLastInput.clear(); + userLastInput.sendKeys(''); + + expect(user.getText()).toContain('{"name":"guest","last":""}'); + expect(lastNameValid.getText()).toContain('true'); + expect(formValid.getText()).toContain('true'); + }); + + it('should be invalid if less than required min length', function() { + userLastInput.clear(); + userLastInput.sendKeys('xx'); + + expect(user.getText()).toContain('{"name":"guest"}'); + expect(lastNameValid.getText()).toContain('false'); + expect(lastNameError.getText()).toContain('minlength'); + expect(formValid.getText()).toContain('false'); + }); + + it('should be invalid if longer than max length', function() { + userLastInput.clear(); + userLastInput.sendKeys('some ridiculously long name'); + + expect(user.getText()).toContain('{"name":"guest"}'); + expect(lastNameValid.getText()).toContain('false'); + expect(lastNameError.getText()).toContain('maxlength'); + expect(formValid.getText()).toContain('false'); + }); + +
    + */ +var inputDirective = ['$browser', '$sniffer', '$filter', '$parse', + function($browser, $sniffer, $filter, $parse) { + return { + restrict: 'E', + require: ['?ngModel'], + link: { + pre: function(scope, element, attr, ctrls) { + if (ctrls[0]) { + (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer, + $browser, $filter, $parse); + } + } + } + }; +}]; + + + +var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/; +/** + * @ngdoc directive + * @name ngValue + * + * @description + * Binds the given expression to the value of `