├── .gitignore
├── .vscode
└── settings.json
├── README.md
├── config
└── systemjs.config.js
├── karma.conf.js
├── notes.txt
├── package.json
├── public
├── admin
│ ├── adminLogin.component.html
│ ├── adminLogin.component.ts
│ ├── createUsers.component.html
│ ├── createUsers.component.ts
│ ├── nameParser.service.ts
│ ├── results.component.html
│ ├── results.component.ts
│ ├── userDetails.component.html
│ ├── userDetails.component.ts
│ ├── userList.component.html
│ └── userList.component.ts
├── app.component.ts
├── app.module.ts
├── app.ts
├── common
│ ├── detailPanel.component.html
│ ├── detailPanel.component.ts
│ ├── talkDuration.pipe.ts
│ └── zoom-in.directive.ts
├── components
│ ├── talkDuration.js
│ └── talkDuration.ts
├── favicon.ico
├── home
│ ├── createNewSession.component.html
│ ├── createNewSession.component.ts
│ ├── home.component.html
│ ├── home.component.ts
│ ├── unreviewedTalk.component.html
│ ├── unreviewedTalk.component.ts
│ └── unreviewedTalk.js
├── index.html
├── loggedIn.guard.ts
├── main.ts
├── nav
│ ├── nav.component.html
│ └── nav.component.ts
├── profile
│ ├── profile.component.html
│ └── profile.component.ts
├── routes.ts
├── rxjsOperations.ts
├── security
│ ├── auth.service.ts
│ ├── currentIdentity.service.ts
│ ├── currentUser.model.ts
│ ├── login.component.html
│ ├── login.component.ts
│ ├── logout.component.ts
│ ├── logout.js
│ └── users.service.ts
├── sessions
│ ├── session.model.ts
│ ├── sessionDetail.component.html
│ ├── sessionDetail.component.ts
│ ├── sessionDetailWithVotes.component.html
│ ├── sessionDetailWithVotes.component.ts
│ ├── sessions.service.ts
│ └── unreviewedSessionCount.service.ts
├── styles.css
├── toastr
│ ├── toastr.js
│ └── toastr.ts
├── upgrades.ts
├── userSessions.resolver.ts
└── vendor
│ ├── 1.4.9
│ ├── angular-mocks.js
│ ├── angular-route.min.js
│ ├── angular.js
│ └── angular.min.js
│ ├── 1.5.5
│ ├── angular-mocks.js
│ ├── angular-route.min.js
│ └── angular.min.js
│ ├── jquery.min.js
│ └── toastr.min.js
├── server
├── auth.js
├── controllers
│ ├── getNextId.js
│ ├── sessionController.js
│ └── userController.js
├── database
│ ├── reviewedSessions.js
│ ├── sessions.js
│ └── users.js
├── expressConfig.js
├── passport.js
├── routes.js
└── server.js
├── test
├── detailPanelSpec.js
├── parseNamesSpec.js
└── userDetailsCtrlSpec.js
├── todo.txt
├── tsconfig.json
└── typings
├── browser.d.ts
└── main.d.ts
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | typings
3 | /public/**/*.js
4 | /public/**/*.map
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Place your settings in this file to overwrite default and user settings.
2 | {
3 | "files.exclude": {
4 | "public/**/*.js": true,
5 | "public/**/*.map": true
6 | }
7 | ,
8 | "typescript.tsdk": "./node_modules/typescript/lib"
9 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | to install & Run:
2 |
3 | npm install
4 | npm install nodemon -g
5 | cd server
6 | nodemon server.js
--------------------------------------------------------------------------------
/config/systemjs.config.js:
--------------------------------------------------------------------------------
1 |
2 | (function(global) {
3 | // map tells the System loader where to look for things
4 | var map = {
5 | 'app': './',
6 | '@angular': 'node_modules/@angular',
7 | 'rxjs': 'node_modules/rxjs'
8 | };
9 | // packages tells the System loader how to load when no filename and/or no extension
10 | var packages = {
11 | 'app': { main: 'main.js', defaultExtension: 'js' },
12 | 'rxjs': { defaultExtension: 'js' }
13 | };
14 | var ngPackageNames = [
15 | 'common',
16 | 'compiler',
17 | 'core',
18 | 'forms',
19 | 'http',
20 | 'platform-browser',
21 | 'platform-browser-dynamic',
22 | 'router',
23 | 'upgrade',
24 | ];
25 | // Individual files (~300 requests):
26 | function packIndex(pkgName) {
27 | packages['@angular/'+pkgName] = { main: 'index.js', defaultExtension: 'js' };
28 | }
29 | // Bundled (~40 requests):
30 | function packUmd(pkgName) {
31 | packages['@angular/'+pkgName] = { main: '/bundles/' + pkgName + '.umd.js', defaultExtension: 'js' };
32 | }
33 | // Most environments should use UMD; some (Karma) need the individual index files
34 | var setPackageConfig = System.packageWithIndex ? packIndex : packUmd;
35 | // Add package entries for angular packages
36 | ngPackageNames.forEach(setPackageConfig);
37 | var config = {
38 | map: map,
39 | packages: packages
40 | };
41 | System.config(config);
42 | })(this);
43 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Sat Apr 16 2016 16:47:26 GMT-0600 (Mountain Daylight Time)
3 |
4 | module.exports = function(config) {
5 | config.set({
6 |
7 | // base path that will be used to resolve all patterns (eg. files, exclude)
8 | basePath: '',
9 |
10 |
11 | // frameworks to use
12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
13 | frameworks: ['jasmine'],
14 |
15 |
16 | // list of files / patterns to load in the browser
17 | files: [
18 | 'public/vendor/jquery.min.js',
19 | 'public/vendor/1.5.5/angular.min.js',
20 | 'public/vendor/1.5.5/angular-route.min.js',
21 | 'public/vendor/toastr.min.js',
22 | 'public/toastr/toastr.js',
23 | 'build/app.js',
24 | 'public/**/*.js',
25 | 'build/**/*.js',
26 | 'test/**/*.js',
27 | 'public/**/*.html'
28 | ],
29 |
30 |
31 | // list of files to exclude
32 | exclude: [
33 | 'public/vendor/1.4.9/**/*.js'
34 | ],
35 |
36 |
37 | // preprocess matching files before serving them to the browser
38 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
39 | preprocessors: {
40 | '**/*.html': ['ng-html2js']
41 | },
42 |
43 | ngHtml2JsPreprocessor: {
44 | stripPrefix: 'public'
45 | },
46 |
47 |
48 | // test results reporter to use
49 | // possible values: 'dots', 'progress'
50 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
51 | reporters: ['progress'],
52 |
53 |
54 | // web server port
55 | port: 9876,
56 |
57 |
58 | // enable / disable colors in the output (reporters and logs)
59 | colors: true,
60 |
61 |
62 | // level of logging
63 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
64 | logLevel: config.LOG_INFO,
65 |
66 |
67 | // enable / disable watching file and executing tests whenever any file changes
68 | autoWatch: true,
69 |
70 |
71 | // start these browsers
72 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
73 | browsers: ['Chrome'],
74 |
75 |
76 | // Continuous Integration mode
77 | // if true, Karma captures browsers, runs the tests and exits
78 | singleRun: false,
79 |
80 | // Concurrency level
81 | // how many browser should be started simultaneous
82 | concurrency: Infinity
83 | })
84 | }
85 |
--------------------------------------------------------------------------------
/notes.txt:
--------------------------------------------------------------------------------
1 | all of these anonymous functions with .bind() are a pain. I need es6 fat arrows
2 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lightning-voter-demo",
3 | "version": "1.0.0",
4 | "description": "lightning talk voting app",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node ./server/server.js",
8 | "dev": "nodemon ./server/server.js -w ./server",
9 | "test": "karma start",
10 | "tsc": "tsc -p . -w"
11 | },
12 | "keywords": [],
13 | "author": "joe eames",
14 | "license": "MIT",
15 | "dependencies": {
16 | "@angular/common": "2.4.1",
17 | "@angular/compiler": "2.4.1",
18 | "@angular/core": "2.4.1",
19 | "@angular/forms": "2.4.1",
20 | "@angular/http": "2.4.1",
21 | "@angular/platform-browser": "2.4.1",
22 | "@angular/platform-browser-dynamic": "2.4.1",
23 | "@angular/router": "3.4.1",
24 | "@angular/upgrade": "2.4.1",
25 | "body-parser": "^1.15.0",
26 | "cookie-parser": "^1.4.1",
27 | "debug": "^2.2.0",
28 | "ejs": "^2.4.1",
29 | "express": "^4.13.4",
30 | "express-session": "^1.13.0",
31 | "morgan": "^1.7.0",
32 | "passport": "^0.3.2",
33 | "passport-local": "^1.0.0",
34 | "core-js": "^2.4.1",
35 | "reflect-metadata": "^0.1.8",
36 | "rxjs": "5.0.1",
37 | "systemjs": "0.19.40",
38 | "zone.js": "^0.7.4"
39 | },
40 | "devDependencies": {
41 | "@types/angular": "^1.6.17",
42 | "@types/core-js": "0.9.36",
43 | "@types/jasmine": "^2.5.36",
44 | "jasmine-core": "^2.4.1",
45 | "karma": "^1.3.0",
46 | "karma-chrome-launcher": "^2.0.0",
47 | "karma-jasmine": "^1.0.2",
48 | "karma-ng-html2js-preprocessor": "^1.0.0",
49 | "typescript": "^2.0.10"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/public/admin/adminLogin.component.html:
--------------------------------------------------------------------------------
1 |
Admin Login
2 |
3 |
--------------------------------------------------------------------------------
/public/admin/adminLogin.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input, Inject } from '@angular/core';
2 | // import { NameParser } from './nameParser.service';
3 | // import { Users } from '../security/users.service';
4 | // import { Observable } from 'rxjs/Rx';
5 | // import { CurrentIdentity } from '../security/currentIdentity.service';
6 | // import { Auth } from '../security/auth.service';
7 |
8 | @Component({
9 | selector: 'admin-login',
10 | templateUrl: "/admin/adminLogin.component.html"
11 | })
12 | export class AdminLoginComponent {
13 | email: string;
14 | password: string;
15 |
16 | constructor(
17 | // @Inject('$location') private $location,
18 | // private currentIdentity: CurrentIdentity,
19 | // private auth: Auth,
20 | // @Inject('toastr') private toastr
21 | ) {
22 |
23 | }
24 |
25 | // ngOnInit() {
26 | // if(this.currentIdentity.authenticated()) {
27 | // this.$location.path('/home');
28 | // }
29 | // }
30 |
31 | // login() {
32 | // this.auth.login({
33 | // username: this.email,
34 | // password: this.password
35 | // }).catch((error:any) => {
36 | // toastr.error(error);
37 | // return Observable.throw(error)
38 | // })
39 | // .subscribe(() => {
40 | // this.$location.path('/home');
41 | // })
42 | // }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/public/admin/createUsers.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Create Users
4 | Enter Email Addresses here. One on each line, First and Last Name Pipe Separated
5 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/public/admin/createUsers.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input, Inject } from '@angular/core';
2 | import { NameParser } from './nameParser.service';
3 | import { Users } from '../security/users.service';
4 | import { Observable } from 'rxjs/Rx';
5 |
6 | @Component({
7 | selector: 'create-users',
8 | templateUrl: "/admin/createUsers.component.html"
9 | })
10 | export class CreateUsersComponent {
11 | namesblob: string;
12 |
13 | constructor(private nameParser: NameParser,
14 | private users: Users,
15 | @Inject('toastr') toastr) {
16 | }
17 |
18 | import() {
19 | var people = this.nameParser.parse(this.namesblob);
20 | people.forEach(person => {
21 | this.users.createNewUser({
22 | email: person.email,
23 | password: "pass",
24 | firstName: person.firstName,
25 | lastName: person.lastName
26 | }).catch(error => {
27 | toastr.error("User already exists: " + person.email)
28 | return Observable.throw("User already exists");
29 | }).subscribe();
30 | });
31 |
32 | toastr.success("Users Created!")
33 | }
34 | }
--------------------------------------------------------------------------------
/public/admin/nameParser.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 |
3 | @Injectable()
4 | export class NameParser {
5 | parse(blobInput) {
6 | var lines = blobInput.split(/\r?\n/);
7 | lines.forEach(function(line, idx) {
8 | var pieces = line.split('|');
9 | lines[idx] = {
10 | email: pieces[0],
11 | firstName: pieces[1],
12 | lastName: pieces[2]
13 | }
14 | })
15 | return lines;
16 | }
17 | }
--------------------------------------------------------------------------------
/public/admin/results.component.html:
--------------------------------------------------------------------------------
1 |
2 | Results
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/public/admin/results.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input, Inject } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'results',
5 | templateUrl: "/admin/results.component.html"
6 | })
7 | export class ResultsComponent {
8 | @Input('allSessions') sessionsByVoteDesc;
9 |
10 | ngOnInit() {
11 | this.sessionsByVoteDesc.sort(function(session1, session2) {
12 | // reverse order
13 | return session2.voteCount - session1.voteCount;
14 | })
15 | }
16 |
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/public/admin/userDetails.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{user.firstName}} {{user.lastName}}
4 | Admin
5 |
6 |
{{user.email}}
7 |
--------------------------------------------------------------------------------
/public/admin/userDetails.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input, Inject } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'user-details',
5 | templateUrl: "/admin/userDetails.component.html"
6 | })
7 | export class UserDetailsComponent {
8 | @Input() allUsers;
9 | user: any;
10 |
11 | constructor(@Inject('$routeParams') private $routeParams) {}
12 |
13 | ngOnInit() {
14 | this.user = this.allUsers.find((user) => {
15 | return user.id === parseInt(this.$routeParams.id);
16 | })
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/public/admin/userList.component.html:
--------------------------------------------------------------------------------
1 |
2 | User List
3 |
4 |
7 | {{user.firstName}}
8 | {{user.lastName}}
9 |
10 |
--------------------------------------------------------------------------------
/public/admin/userList.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'user-list',
5 | templateUrl: "/admin/userList.component.html"
6 | })
7 | export class UserListComponent {
8 | @Input('allUsers') users;
9 |
10 | ngOnInit() {
11 | this.users.sort(function(user1, user2) {
12 | if(user1.firstName < user2.firstName) return -1;
13 | if(user1.firstName === user2.firstName) return 0;
14 | if(user1.firstName > user2.firstName) return 1;
15 | })
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/public/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'my-app',
5 |
6 | template: `
7 |
8 | `
9 | })
10 | export class AppComponent {
11 | }
--------------------------------------------------------------------------------
/public/app.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule, forwardRef } from '@angular/core';
2 | import { BrowserModule } from '@angular/platform-browser';
3 | import { FormsModule } from '@angular/forms';
4 | import { HttpModule } from '@angular/http';
5 |
6 | import { HomeComponent } from './home/home.component';
7 | import { NameParser } from './admin/nameParser.service';
8 | import { UnreviewedSessionCount } from './sessions/unreviewedSessionCount.service';
9 |
10 | import { Sessions } from './sessions/sessions.service';
11 | import { Users } from './security/users.service';
12 | import { Auth } from './security/auth.service';
13 | import { CurrentIdentity } from './security/currentIdentity.service';
14 | import { UnreviewedTalkComponent } from './home/unreviewedTalk.component';
15 | import { NavComponent } from './nav/nav.component';
16 | import { ZoomInDirective } from './common/zoom-in.directive';
17 | import { TalkDurationPipe } from './common/talkDuration.pipe';
18 | import { DetailPanelComponent } from './common/detailPanel.component'
19 | import { CreateNewSessionComponent } from './home/createNewSession.component';
20 | import { ProfileComponent } from './profile/profile.component';
21 | import { SessionDetailWithVotesComponent } from './sessions/sessionDetailWithVotes.component';
22 | import { SessionDetailComponent } from './sessions/sessionDetail.component';
23 | import { LogoutComponent } from './security/logout.component';
24 | import { LoginComponent } from './security/login.component';
25 | import { UserListComponent } from './admin/userList.component';
26 | import { UserDetailsComponent } from './admin/userDetails.component';
27 | import { ResultsComponent } from './admin/results.component';
28 | import { CreateUsersComponent } from './admin/createUsers.component';
29 | import { AdminLoginComponent } from './admin/adminLogin.component';
30 | import { routing } from './routes';
31 | import { LoggedInGuard } from './loggedIn.guard';
32 | import { SessionResolver } from './userSessions.resolver';
33 | import { AppComponent } from './app.component';
34 |
35 | import { UpgradeAdapter, UpgradeAdapterRef } from '@angular/upgrade';
36 |
37 | // This is really strange. But you need the module when you create
38 | // the upgrade adapter, but you also need the created upgrade adapter
39 | // when creating the Module (see below, the call to upgradeNg1Component)
40 | // so we use a forwardRef, and we have to put the upgradeAdapter and the
41 | // module in the same file
42 | export const upgradeAdapter = new UpgradeAdapter(forwardRef(() => AppModule));
43 |
44 | @NgModule({
45 | imports: [
46 | BrowserModule,
47 | FormsModule,
48 | HttpModule,
49 | routing
50 | ],
51 | declarations: [
52 | AppComponent,
53 | HomeComponent,
54 | DetailPanelComponent,
55 | CreateNewSessionComponent,
56 | ProfileComponent,
57 | SessionDetailWithVotesComponent,
58 | SessionDetailComponent,
59 | UnreviewedTalkComponent,
60 | NavComponent,
61 | LogoutComponent,
62 | LoginComponent,
63 | UserListComponent,
64 | UserDetailsComponent,
65 | ResultsComponent,
66 | CreateUsersComponent,
67 | AdminLoginComponent,
68 |
69 | ZoomInDirective,
70 | TalkDurationPipe
71 | ],
72 | providers: [
73 | NameParser,
74 | Sessions,
75 | UnreviewedSessionCount,
76 | Users,
77 | Auth,
78 | CurrentIdentity,
79 | { provide: 'toastr', useValue: toastr },
80 | LoggedInGuard,
81 | SessionResolver
82 | ],
83 | bootstrap: [
84 | AppComponent
85 | ]
86 | })
87 | export class AppModule { }
88 |
--------------------------------------------------------------------------------
/public/app.ts:
--------------------------------------------------------------------------------
1 | // var app = angular.module('app', ['ngRoute', 'toastr']);
2 | // app.run(function($rootScope, $location) {
3 | // $rootScope.$on("$routeChangeError", function(e, next, prev, err) {
4 | // if(err === "AUTH_REQUIRED") {
5 | // $location.path("/login");
6 | // }
7 | // if(err === 'NOT_AUTHORIZED') {
8 | // $location.path("/home");
9 | // }
10 | // })
11 | // })
12 |
--------------------------------------------------------------------------------
/public/common/detailPanel.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{title}}
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/public/common/detailPanel.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Inject, Input, Output, EventEmitter } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'detail-panel',
5 | templateUrl: '/common/detailPanel.component.html'
6 | })
7 | export class DetailPanelComponent {
8 | @Input() title: string;
9 | @Input('collapsed') initialCollapsed;
10 | collapsed: boolean;
11 |
12 | ngOnChanges() {
13 | this.collapsed = (this.initialCollapsed === 'true');
14 | }
15 |
16 | collapse() {
17 | this.collapsed = !this.collapsed;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/public/common/talkDuration.pipe.ts:
--------------------------------------------------------------------------------
1 | import { Pipe, PipeTransform } from '@angular/core'
2 |
3 | @Pipe({name: 'talkDuration'})
4 | export class TalkDurationPipe implements PipeTransform {
5 | transform(duration: string): string {
6 | return "Duration: " + duration + " minutes";
7 | }
8 | }
--------------------------------------------------------------------------------
/public/common/zoom-in.directive.ts:
--------------------------------------------------------------------------------
1 | import { Directive, Input, ElementRef } from '@angular/core';
2 |
3 | @Directive({
4 | selector: '[zoom-in]',
5 | })
6 | export class ZoomInDirective {
7 | private el: HTMLElement;
8 |
9 | constructor(el: ElementRef) {
10 | this.el = el.nativeElement;
11 |
12 | this.el.addEventListener('mouseenter', e => {
13 | this.el.style.transform="scale(1.1,1.1)";
14 | })
15 | this.el.addEventListener('mouseleave', e => {
16 | this.el.style.transform="scale(1,1)";
17 | })
18 |
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/public/components/talkDuration.js:
--------------------------------------------------------------------------------
1 | angular.module('app').filter('talkDuration', function () {
2 | return function (duration) {
3 | return "Duration: " + duration + " minutes";
4 | };
5 | });
6 | //# sourceMappingURL=talkDuration.js.map
--------------------------------------------------------------------------------
/public/components/talkDuration.ts:
--------------------------------------------------------------------------------
1 | angular.module('app').filter('talkDuration', function() {
2 | return function(duration) {
3 | return "Duration: " + duration + " minutes";
4 | }
5 | })
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joeeames/lightning-voter-demo/8edb58947b02425b126a3e618886002cfb1b2c6b/public/favicon.ico
--------------------------------------------------------------------------------
/public/home/createNewSession.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Create New Session
4 |
5 |
29 |
30 | Your Other Sessions
31 |
32 |
33 |
--------------------------------------------------------------------------------
/public/home/createNewSession.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Inject, Input, Output, EventEmitter } from '@angular/core';
2 | import { Sessions } from '../sessions/sessions.service';
3 | import { Response } from '@angular/http';
4 | import { Session } from '../sessions/session.model';
5 | import { NavComponent } from '../nav/nav.component';
6 | import { CurrentIdentity } from '../security/currentIdentity.service';
7 |
8 | @Component({
9 | selector: 'create-new-session',
10 | templateUrl: '/home/createNewSession.component.html',
11 | // directives: [NavComponent]
12 | })
13 | export class CreateNewSessionComponent {
14 | @Input() userSessions: Session[];
15 | title: string;
16 | length: string;
17 | abstract: string;
18 |
19 | constructor(
20 | @Inject('toastr') private toastr:any,
21 | private currentIdentity:CurrentIdentity,
22 | private sessions: Sessions) {
23 |
24 | }
25 |
26 | create(newUserSession:Session) {
27 | newUserSession.userFirstName = this.currentIdentity.currentUser.firstName,
28 | newUserSession.userLastName = this.currentIdentity.currentUser.lastName,
29 | newUserSession.userId = this.currentIdentity.currentUser.id,
30 |
31 | this.sessions.createNewSession(newUserSession)
32 | .subscribe((data: Session) => {
33 | this.userSessions.push(data);
34 | })
35 |
36 | }
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/public/home/home.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Unreviewed Sessions
4 |
5 |
6 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/public/home/home.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Inject, Input, Output, EventEmitter } from '@angular/core';
2 | import { Sessions } from '../sessions/sessions.service';
3 | import { UnreviewedTalkComponent } from './unreviewedTalk.component';
4 | import { ZoomInDirective } from '../common/zoom-in.directive';
5 | import { Session } from '../sessions/session.model';
6 | import { CurrentUser } from '../security/currentUser.model';
7 | import { NavComponent } from '../nav/nav.component';
8 | import { UnreviewedSessionCount } from '../sessions/unreviewedSessionCount.service';
9 | import { CurrentIdentity } from '../security/currentIdentity.service';
10 | import { ActivatedRouteSnapshot, ActivatedRoute } from '@angular/router';
11 |
12 | @Component({
13 | selector: 'home',
14 | templateUrl: '/home/home.component.html',
15 | })
16 | export class HomeComponent {
17 | @Input() userSessions: Session[];
18 | @Input() b: number;
19 | currentUser: CurrentUser;
20 | currentSessionToReview: Session;
21 |
22 | constructor(
23 | private currentIdentity: CurrentIdentity,
24 | private sessions : Sessions,
25 | @Inject('toastr') private toastr,
26 | private route: ActivatedRoute,
27 | private unreviewedSessionCount: UnreviewedSessionCount) {
28 | console.log('home');
29 | this.currentUser = currentIdentity.currentUser;
30 | this.setNextSessionToReview();
31 |
32 | this.route.data.subscribe(val => console.log('subscribe', val));
33 | }
34 |
35 | ngOnInit() {
36 | console.log(this.route.snapshot.data['userSessions'].length);
37 | this.userSessions = this.route.snapshot.data['userSessions'];
38 | }
39 |
40 | ngOnChanges() {
41 | console.log('changes', this.b, this.userSessions);
42 | }
43 |
44 | setNextSessionToReview() {
45 | this.sessions.getNextUnreviewedSession(this.currentIdentity.currentUser.id)
46 | .subscribe((session:Session) => {
47 | this.currentSessionToReview = session;
48 | })
49 | }
50 |
51 | voteYes() {
52 | this.sessions.incrementVote(this.currentSessionToReview.id)
53 | .flatMap(() => this.sessions.addReviewedSession(this.currentUser.id, this.currentSessionToReview.id))
54 | .subscribe(() => {
55 | this.setNextSessionToReview();
56 |
57 | // pull updated value
58 | this.unreviewedSessionCount.updateUnreviewedSessionCount();
59 | })
60 | }
61 |
62 | voteNo() {
63 | this.sessions.addReviewedSession(this.currentUser.id, this.currentSessionToReview.id)
64 | .subscribe(() => {
65 | this.setNextSessionToReview();
66 |
67 | // pull updated value
68 | this.unreviewedSessionCount.updateUnreviewedSessionCount();
69 | })
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/public/home/unreviewedTalk.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{session.title}}
5 |
6 |
7 |
{{session.length | talkDuration}}
8 |
{{session.abstract}}
9 |
10 |
11 |
12 |
Are you interested in this session?
13 |
14 |
15 |
16 |
17 | You have reviewed all the submitted sessions
18 |
--------------------------------------------------------------------------------
/public/home/unreviewedTalk.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Inject, Input, Output, EventEmitter } from '@angular/core';
2 | import { TalkDurationPipe } from '../common/talkDuration.pipe';
3 |
4 | @Component({
5 | selector: 'unreviewed-talk',
6 | templateUrl: '/home/unreviewedTalk.component.html',
7 | })
8 | export class UnreviewedTalkComponent {
9 | @Input() session: any;
10 | @Output() voteYes = new EventEmitter();
11 | @Output() voteNo = new EventEmitter();
12 |
13 | yes() {
14 | this.voteYes.emit(null);
15 | }
16 |
17 | no() {
18 | this.voteNo.emit(null);
19 | }
20 | }
--------------------------------------------------------------------------------
/public/home/unreviewedTalk.js:
--------------------------------------------------------------------------------
1 | System.register(['@angular/core', '../common/talkDuration.pipe'], function(exports_1, context_1) {
2 | "use strict";
3 | var __moduleName = context_1 && context_1.id;
4 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
5 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
6 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
7 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
8 | return c > 3 && r && Object.defineProperty(target, key, r), r;
9 | };
10 | var __metadata = (this && this.__metadata) || function (k, v) {
11 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
12 | };
13 | var core_1, talkDuration_pipe_1;
14 | var UnreviewedTalkComponent;
15 | return {
16 | setters:[
17 | function (core_1_1) {
18 | core_1 = core_1_1;
19 | },
20 | function (talkDuration_pipe_1_1) {
21 | talkDuration_pipe_1 = talkDuration_pipe_1_1;
22 | }],
23 | execute: function() {
24 | UnreviewedTalkComponent = (function () {
25 | function UnreviewedTalkComponent() {
26 | this.voteYes = new core_1.EventEmitter();
27 | this.voteNo = new core_1.EventEmitter();
28 | }
29 | UnreviewedTalkComponent.prototype.yes = function () {
30 | this.voteYes.emit(null);
31 | };
32 | UnreviewedTalkComponent.prototype.no = function () {
33 | this.voteNo.emit(null);
34 | };
35 | __decorate([
36 | core_1.Input(),
37 | __metadata('design:type', Object)
38 | ], UnreviewedTalkComponent.prototype, "session", void 0);
39 | __decorate([
40 | core_1.Output(),
41 | __metadata('design:type', Object)
42 | ], UnreviewedTalkComponent.prototype, "voteYes", void 0);
43 | __decorate([
44 | core_1.Output(),
45 | __metadata('design:type', Object)
46 | ], UnreviewedTalkComponent.prototype, "voteNo", void 0);
47 | UnreviewedTalkComponent = __decorate([
48 | core_1.Component({
49 | selector: 'unreviewed-talk',
50 | templateUrl: '/home/unreviewedTalk.html',
51 | pipes: [talkDuration_pipe_1.TalkDurationPipe]
52 | }),
53 | __metadata('design:paramtypes', [])
54 | ], UnreviewedTalkComponent);
55 | return UnreviewedTalkComponent;
56 | }());
57 | exports_1("UnreviewedTalkComponent", UnreviewedTalkComponent);
58 | }
59 | }
60 | });
61 | //# sourceMappingURL=unreviewedTalk.js.map
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ng-conf Lightning Talks
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
35 |
36 |
--------------------------------------------------------------------------------
/public/loggedIn.guard.ts:
--------------------------------------------------------------------------------
1 | import { CanActivate, Router } from '@angular/router';
2 | import { Injectable } from '@angular/core';
3 | import { Observable } from 'rxjs/Rx';
4 | import { Auth } from './security/auth.service';
5 |
6 | @Injectable()
7 | export class LoggedInGuard implements CanActivate {
8 |
9 | constructor(private auth: Auth,
10 | private router: Router) {}
11 |
12 | canActivate() {
13 | var o = this.auth.requireLogin();
14 |
15 | o.subscribe(loggedIn => {
16 | if(!loggedIn) {
17 | this.router.navigate(['/login'])
18 | }
19 | });
20 | return o;
21 | }
22 | }
--------------------------------------------------------------------------------
/public/main.ts:
--------------------------------------------------------------------------------
1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
2 | import { AppModule } from './app.module';
3 | // this is done to make sure that typescript knows about all the rxjs operations
4 | import './rxjsOperations';
5 |
6 |
7 | platformBrowserDynamic().bootstrapModule(AppModule)
8 |
--------------------------------------------------------------------------------
/public/nav/nav.component.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
15 |
16 |
17 | -
18 | Welcome {{currentUser.firstName}} {{currentUser.lastName}}
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/public/nav/nav.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Inject, Input, Output, EventEmitter, ChangeDetectorRef } from '@angular/core';
2 | import { Sessions } from '../sessions/sessions.service';
3 | import { CurrentUser } from '../security/currentUser.model';
4 | import { UnreviewedSessionCount } from '../sessions/unreviewedSessionCount.service';
5 | import { CurrentIdentity } from '../security/currentIdentity.service';
6 |
7 | @Component({
8 | selector: 'nav',
9 | templateUrl: '/nav/nav.component.html'
10 | })
11 | export class NavComponent {
12 |
13 | currentUser: CurrentUser;
14 |
15 | constructor(
16 | private currentIdentity: CurrentIdentity,
17 | private sessions : Sessions,
18 | @Inject('toastr') private toastr,
19 | private unreviewedSessionCount: UnreviewedSessionCount,
20 | private changeDetectorRef: ChangeDetectorRef) {
21 |
22 | this.currentUser = currentIdentity.currentUser;
23 |
24 | unreviewedSessionCount.updateUnreviewedSessionCount(function() {
25 | changeDetectorRef.detectChanges();
26 | });
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/public/profile/profile.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | User Profile
4 |
5 |
--------------------------------------------------------------------------------
/public/profile/profile.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Inject } from '@angular/core';
2 | import { NavComponent } from '../nav/nav.component';
3 | import { CurrentIdentity } from '../security/currentIdentity.service';
4 | import { Router } from '@angular/router';
5 |
6 | @Component({
7 | selector: 'profile',
8 | templateUrl: '/profile/profile.component.html'
9 | })
10 | export class ProfileComponent {
11 |
12 | profile: any;
13 |
14 | constructor(
15 | private currentIdentity:CurrentIdentity,
16 | private router: Router,
17 | @Inject('toastr') private toastr:any
18 | ) {
19 |
20 | this.currentIdentity = currentIdentity;
21 | }
22 |
23 |
24 | save(newProfile) {
25 | this.currentIdentity.updateUser(newProfile);
26 | toastr.success('Profile Saved!');
27 | }
28 |
29 | cancel() {
30 | this.router.navigate(['/home']);
31 | }
32 |
33 | }
--------------------------------------------------------------------------------
/public/routes.ts:
--------------------------------------------------------------------------------
1 | import { CanActivate, Routes, RouterModule } from '@angular/router';
2 | import { Injectable, ModuleWithProviders } from '@angular/core';
3 | import { Observable } from 'rxjs/Rx';
4 |
5 | import { AdminLoginComponent } from './admin/adminLogin.component';
6 | import { ResultsComponent } from './admin/results.component';
7 | import { UserDetailsComponent } from './admin/userDetails.component';
8 | import { UserListComponent } from './admin/userList.component';
9 | import { CreateUsersComponent } from './admin/createUsers.component';
10 | import { HomeComponent } from './home/home.component';
11 | import { CreateNewSessionComponent } from './home/createNewSession.component';
12 | import { ProfileComponent } from './profile/profile.component';
13 | import { LoginComponent } from './security/login.component';
14 | import { LogoutComponent } from './security/logout.component';
15 | import { Auth } from './security/auth.service';
16 | import { LoggedInGuard } from './loggedIn.guard';
17 | import { SessionResolver } from './userSessions.resolver';
18 |
19 |
20 | export const appRoutes: Routes = [
21 | { path: 'profile', component: ProfileComponent },
22 | { path: 'login', component: LoginComponent },
23 | { path: 'logout', component: LogoutComponent },
24 | { path: 'createsession', component: CreateNewSessionComponent },
25 | { path: 'home', component: HomeComponent, canActivate: [LoggedInGuard], resolve: { userSessions: SessionResolver} },
26 | { path: 'admin/createusers', component: CreateUsersComponent },
27 | { path: 'users', component: UserListComponent },
28 | { path: 'admin/users/:id', component: UserDetailsComponent },
29 | { path: 'admin/results', component: ResultsComponent },
30 | { path: 'admin/login', component: AdminLoginComponent },
31 |
32 | { path: '', redirectTo: "/home", pathMatch: "full"}
33 | ]
34 |
35 | export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes, { useHash: true });
36 |
37 |
38 | /*
39 |
40 | app.config(function($routeProvider) {
41 | var routeResolvers = {
42 | loggedIn: function(auth, $q) {
43 | return auth.requireLogin().toPromise();
44 | },
45 | waitForAuth: function(auth) {
46 | return auth.waitForAuth().toPromise();
47 | },
48 | requireAdmin: function(auth, $q) {
49 | return auth.requireAdmin().toPromise();
50 | },
51 | userSessions: function(sessions, currentIdentity, auth, $q) {
52 | return auth.requireLogin().toPromise().then(function() {
53 | return sessions
54 | .getSessionsByUser(currentIdentity.currentUser.id)
55 | .toPromise();
56 | });
57 | },
58 | allSessions: function(sessions, auth, $q) {
59 | return auth.requireLogin().toPromise().then(function() {
60 | return sessions.getAllSessions().toPromise();
61 | });
62 | },
63 | allUsers: function(users, auth, $q) {
64 | return auth.requireLogin().toPromise().then(function() {
65 | return users.getAllUsers().toPromise();
66 | });
67 | }
68 |
69 | }
70 |
71 | $routeProvider
72 | .when('/admin/login', {
73 | template: '',
74 | resolve: {
75 | currentAuth: routeResolvers.waitForAuth
76 | }
77 | })
78 | .when('/admin/results', {
79 | template: '',
80 | resolve: {
81 | admin: routeResolvers.requireAdmin,
82 | allSessions: routeResolvers.allSessions
83 | }
84 | })
85 | .when('/admin/users/:id', {
86 | template: '',
87 | resolve: {
88 | admin: routeResolvers.requireAdmin,
89 | allUsers: routeResolvers.allUsers
90 | }
91 | })
92 | .when('/users', {
93 | template: '',
94 | resolve: {
95 | admin: routeResolvers.requireAdmin,
96 | allUsers: routeResolvers.allUsers
97 | }
98 | })
99 | .when('/admin/createusers', {
100 | template: '',
101 | resolve: {
102 | admin: routeResolvers.requireAdmin
103 | }
104 | })
105 | .when('/home', {
106 | template: '',
107 | resolve: {
108 | login:routeResolvers.loggedIn,
109 | userSessions: routeResolvers.userSessions
110 | }
111 | })
112 | .when('/profile', {
113 | template: '',
114 | resolve: {
115 | userProfile: routeResolvers.loggedIn,
116 | }
117 | })
118 | .when('/createsession', {
119 | template: '',
120 | resolve: {
121 | userSessions: routeResolvers.userSessions,
122 | }
123 | })
124 | .when('/login', {
125 | template: '',
126 | resolve: {
127 | currentAuth: routeResolvers.waitForAuth
128 | }
129 | })
130 | .when('/logout', {
131 | template: ''
132 | })
133 | .otherwise('/home')
134 | })*/
--------------------------------------------------------------------------------
/public/rxjsOperations.ts:
--------------------------------------------------------------------------------
1 | import 'rxjs/add/operator/catch';
2 | import 'rxjs/add/operator/do';
3 | import 'rxjs/add/operator/finally';
4 | import 'rxjs/add/operator/map';
5 | import 'rxjs/add/observable/of';
6 | import 'rxjs/add/observable/fromPromise';
7 | import 'rxjs/add/operator/toPromise';
8 | import 'rxjs/add/operator/mergeMap';
--------------------------------------------------------------------------------
/public/security/auth.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, Inject } from '@angular/core';
2 | import { Http, Response } from '@angular/http';
3 | import { Observable } from 'rxjs/Rx';
4 | import { CurrentIdentity } from './currentIdentity.service'
5 |
6 | @Injectable()
7 | export class Auth {
8 |
9 | constructor(private http: Http,
10 | private currentIdentity: CurrentIdentity) {
11 | }
12 |
13 | login(credentials) {
14 | return this.http.post('/api/login', credentials)
15 | .catch((error:any) => {
16 | return Observable.throw("Invalid Credentials");
17 | })
18 | .map((response:Response) => {
19 | this.currentIdentity.setUser(response.json().user);
20 | return this.currentIdentity;
21 | });
22 | }
23 |
24 | logout() {
25 | this.http.post('/api/logout', {})
26 | .catch(() => Observable.throw("Error Logging Out"))
27 | .subscribe((response: Response) => {
28 | this.currentIdentity.clearUser();
29 | })
30 | }
31 |
32 | waitForAuth() {
33 | return this.http.get('/api/currentIdentity')
34 | .map((response: Response) => {
35 | if(!!response.text()) {
36 | this.currentIdentity.setUser(response.json());
37 | }
38 | return this.currentIdentity;
39 | })
40 | }
41 |
42 | requireLogin() {
43 | return this.waitForAuth().flatMap((a:any) => {
44 | if(this.currentIdentity.authenticated()) {
45 | return Observable.of(true);
46 | } else {
47 | return Observable.throw('AUTH_REQUIRED');
48 | }
49 | })
50 | }
51 |
52 | requireAdmin() {
53 | return this.waitForAuth().flatMap(() => {
54 | if(this.currentIdentity.authenticated() && this.currentIdentity.currentUser.isAdmin) {
55 | return Observable.of(true);
56 | } else {
57 | return Observable.throw('AUTH_REQUIRED');
58 | }
59 | })
60 | }
61 | }
--------------------------------------------------------------------------------
/public/security/currentIdentity.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, Inject } from '@angular/core';
2 | import { Http, Response } from '@angular/http';
3 | import { Observable } from 'rxjs/Rx';
4 |
5 | //remove this when this gets converted to ng2
6 | export interface CurrentUser {
7 | firstName: string;
8 | lastName: string;
9 | id: number;
10 | isAdmin: boolean;
11 | }
12 |
13 | @Injectable()
14 | export class CurrentIdentity {
15 | currentUser: CurrentUser;
16 |
17 | constructor(private http: Http) {
18 | this.currentUser = null;
19 | }
20 |
21 | setUser(user) {
22 | this.currentUser = user;
23 | }
24 |
25 | clearUser() {
26 | this.currentUser = null;
27 | }
28 |
29 | authenticated() {
30 | return !!this.currentUser;
31 | }
32 |
33 | updateUser(newUserObj) {
34 | this.http.put('/api/users/' + this.currentUser.id, newUserObj)
35 | .map((response:Response) => {
36 | this.currentUser.firstName = newUserObj.firstName;
37 | this.currentUser.lastName = newUserObj.lastName;
38 | })
39 | .subscribe()
40 | }
41 | };
--------------------------------------------------------------------------------
/public/security/currentUser.model.ts:
--------------------------------------------------------------------------------
1 | export class CurrentUser {
2 | firstName: string;
3 | lastName: string;
4 | id: number;
5 | }
--------------------------------------------------------------------------------
/public/security/login.component.html:
--------------------------------------------------------------------------------
1 | Please Login
2 |
3 | Enter your attendee email address
4 |
--------------------------------------------------------------------------------
/public/security/login.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Inject } from '@angular/core';
2 | import { CurrentIdentity } from './currentIdentity.service';
3 | import { Observable } from 'rxjs/Rx';
4 | import { Auth } from './auth.service';
5 | import { Router } from '@angular/router';
6 |
7 | @Component({
8 | selector: 'login',
9 | templateUrl: "/security/login.component.html"
10 | })
11 | export class LoginComponent {
12 | email: string;
13 |
14 | constructor(
15 | private router: Router,
16 | currentIdentity:CurrentIdentity,
17 | private auth: Auth,
18 | @Inject('toastr') private toastr) {
19 |
20 | if(currentIdentity.authenticated()) {
21 | this.router.navigate(['/home']);
22 | }
23 | }
24 |
25 | login() {
26 | this.auth.login({
27 | username: this.email,
28 | password: "pass"
29 | }).catch((error:any) => {
30 | this.toastr.error(error);
31 | return Observable.throw(error)
32 | }).subscribe(() => {
33 | this.router.navigate(['/home']);
34 | })
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/public/security/logout.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Inject } from '@angular/core';
2 | import { Auth } from './auth.service';
3 | import { Router } from '@angular/router';
4 |
5 | @Component({
6 | selector: 'logout',
7 | template: ""
8 | })
9 | export class LogoutComponent {
10 |
11 | constructor(
12 | private router: Router,
13 | private auth: Auth) {
14 |
15 | }
16 |
17 | ngOnInit() {
18 | this.auth.logout();
19 | this.router.navigate(['/login']);
20 | }
21 | }
--------------------------------------------------------------------------------
/public/security/logout.js:
--------------------------------------------------------------------------------
1 | angular.module('app').component('logout', {
2 | controller: function ($location, auth) {
3 | auth.logout();
4 | $location.path('/login');
5 | }
6 | });
7 | //# sourceMappingURL=logout.js.map
--------------------------------------------------------------------------------
/public/security/users.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, Inject } from '@angular/core';
2 | import { Http, Response } from '@angular/http';
3 | import { Observable } from 'rxjs/Rx';
4 |
5 | @Injectable()
6 | export class Users {
7 |
8 | constructor(private http: Http) {
9 | }
10 |
11 | createNewUser(newUser) {
12 | return this.http.post('/api/users', newUser);
13 | }
14 |
15 | getAllUsers() {
16 | return this.http.get('/api/users').map((response: Response) => {
17 | return response.json();
18 | })
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/public/sessions/session.model.ts:
--------------------------------------------------------------------------------
1 | export class Session {
2 | id: number;
3 | title: string;
4 | abstract: string;
5 | userId: number;
6 | voteCount: number;
7 | length: number;
8 | userFirstName: string;
9 | userLastName: string;
10 | }
--------------------------------------------------------------------------------
/public/sessions/sessionDetail.component.html:
--------------------------------------------------------------------------------
1 |
2 | {{session.length | talkDuration}}
3 | {{session.abstract}}
4 |
5 |
--------------------------------------------------------------------------------
/public/sessions/sessionDetail.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Inject, Input } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'session-detail',
5 | templateUrl: '/sessions/sessionDetail.component.html'
6 | })
7 | export class SessionDetailComponent {
8 | @Input() session: any;
9 | @Input() initialCollapsed: any;
10 | }
11 |
--------------------------------------------------------------------------------
/public/sessions/sessionDetailWithVotes.component.html:
--------------------------------------------------------------------------------
1 |
2 | {{session.voteCount}} votes
3 | {{session.length | talkDuration}}
4 | {{session.abstract}}
5 |
6 |
--------------------------------------------------------------------------------
/public/sessions/sessionDetailWithVotes.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Inject, Input } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'session-detail-with-votes',
5 | templateUrl: '/sessions/sessionDetailWithVotes.component.html'
6 | })
7 | export class SessionDetailWithVotesComponent {
8 | @Input() session: any;
9 | @Input() initialCollapsed: any;
10 | }
11 |
--------------------------------------------------------------------------------
/public/sessions/sessions.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, Inject } from '@angular/core';
2 | import { Http, Response } from '@angular/http';
3 | import { Observable } from 'rxjs/Rx';
4 | import { Session } from './session.model';
5 |
6 | @Injectable()
7 | export class Sessions {
8 |
9 | constructor(private http: Http) {
10 | }
11 |
12 | getSessionsByUser(userId) {
13 | return this.http.get('/api/sessions/user/' + userId)
14 | .map((rsp: Response) => {
15 | return rsp.json();
16 | });
17 | }
18 |
19 | createNewSession(newSession) {
20 | return this.http.post('/api/sessions', newSession)
21 | .map((rsp: Response) => {
22 | return rsp.json();
23 | });
24 | }
25 |
26 | getNextUnreviewedSession(userId) {
27 | return this.http
28 | .get(`/api/users/${userId}/randomUnreviewedSession`)
29 | .map((rsp: Response) => {
30 | if(rsp.text() !== "")
31 | return rsp.json();
32 | else
33 | return null;
34 | });
35 | }
36 |
37 | addReviewedSession(userId, sessionId) {
38 | return this.http.post('/api/users/' + userId + '/reviewSession/' + sessionId, null);
39 | }
40 |
41 | getAllSessions() {
42 | return this.http.get('/api/sessions/')
43 | .map((rsp: Response) => {
44 | return rsp.json();
45 | });
46 | }
47 |
48 | incrementVote(sessionId) {
49 | return this.http.put('/api/sessions/' + sessionId + '/incrementVote/', null);
50 | }
51 |
52 | getUnreviewedCount(userId) {
53 | return this.http.get('/api/users/' + userId + '/unreviewedSessionCount');
54 | }
55 | };
--------------------------------------------------------------------------------
/public/sessions/unreviewedSessionCount.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, Inject } from '@angular/core';
2 | import { Http, Response } from '@angular/http';
3 | import { Observable } from 'rxjs/Rx';
4 | import { Session } from './session.model';
5 | import { Sessions } from './sessions.service';
6 | import { CurrentIdentity } from '../security/currentIdentity.service';
7 |
8 | @Injectable()
9 | export class UnreviewedSessionCount {
10 | value: number;
11 |
12 | constructor(private sessions: Sessions,
13 | private currentIdentity:CurrentIdentity) {
14 | this.value = 0;
15 | }
16 |
17 | updateUnreviewedSessionCount(cb?) {
18 | this.sessions.getUnreviewedCount(this.currentIdentity.currentUser.id)
19 | .subscribe(response => {
20 |
21 | this.value = response.json().count;
22 | if(cb) {
23 | cb();
24 | }
25 | })
26 | }
27 | }
28 |
29 |
30 |
--------------------------------------------------------------------------------
/public/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top:80px;
3 | }
4 |
5 | .pointable {
6 | cursor: pointer;
7 | }
8 |
9 | .small-well {
10 | min-height: 15px;
11 | padding: 10px;
12 | margin-bottom: 10px;
13 | background-color: #f5f5f5;
14 | border: 1px solid #e3e3e3;
15 | border-radius: 3px;
16 | }
17 |
18 | .btn-spaced {
19 | margin-right:15px;
20 | }
--------------------------------------------------------------------------------
/public/toastr/toastr.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var toastrModule = angular.module('toastr', []);
3 | toastr.options.timeOut = 1000;
4 | toastrModule.value('toastr', toastr);
5 | }());
6 | //# sourceMappingURL=toastr.js.map
--------------------------------------------------------------------------------
/public/toastr/toastr.ts:
--------------------------------------------------------------------------------
1 | declare var toastr;
2 |
3 | (function() {
4 | var toastrModule = angular.module('toastr', []);
5 |
6 | toastr.options.timeOut = 1000;
7 |
8 | toastrModule.value('toastr', toastr);
9 |
10 | }())
--------------------------------------------------------------------------------
/public/upgrades.ts:
--------------------------------------------------------------------------------
1 | // import { upgradeAdapter } from './app.module';
2 | // import { NameParser } from './admin/nameParser.service';
3 | // import { Sessions } from './sessions/sessions.service';
4 | // import { Users } from './security/users.service';
5 | // import { CurrentIdentity } from './security/currentIdentity.service';
6 | // import { ProfileComponent } from './profile/profile.component';
7 | // import { UnreviewedTalkComponent } from './home/unreviewedTalk.component';
8 | // import { CreateNewSessionComponent } from './home/createNewSession.component';
9 | // import { DetailPanelComponent } from './common/detailPanel.component';
10 | // import { HomeComponent } from './home/home.component';
11 | // import { NavComponent } from './nav/nav.component';
12 | // import { SessionDetailWithVotesComponent } from './sessions/sessionDetailWithVotes.component';
13 | // import { SessionDetailComponent } from './sessions/sessionDetail.component';
14 | // import { LogoutComponent } from './security/logout.component';
15 | // import { LoginComponent } from './security/login.component';
16 | // import { UserListComponent } from './admin/userList.component';
17 | // import { Auth } from './security/auth.service';
18 | // import { UserDetailsComponent } from './admin/userDetails.component';
19 | // import { ResultsComponent } from './admin/results.component';
20 | // import { CreateUsersComponent } from './admin/createUsers.component';
21 | // import { AdminLoginComponent } from './admin/adminLogin.component';
22 |
23 | // export function upgradeAndDowngrade() {
24 |
25 | // // Downgrading Angular 2 services to Angular 1 services
26 | // angular.module('app').factory('nameParser', upgradeAdapter.downgradeNg2Provider(NameParser))
27 | // angular.module('app').factory('sessions', upgradeAdapter.downgradeNg2Provider(Sessions))
28 | // angular.module('app').factory('users', upgradeAdapter.downgradeNg2Provider(Users))
29 | // angular.module('app').factory('unreviewedSessionCount', upgradeAdapter.downgradeNg2Provider(Sessions))
30 | // angular.module('app').factory('currentIdentity', upgradeAdapter.downgradeNg2Provider(CurrentIdentity))
31 | // angular.module('app').factory('auth', upgradeAdapter.downgradeNg2Provider(Auth))
32 |
33 | // // Upgrading Angular 1 services to Angular 2 services
34 | // upgradeAdapter.upgradeNg1Provider("$location");
35 | // upgradeAdapter.upgradeNg1Provider("$routeParams");
36 | // // upgradeAdapter.upgradeNg1Provider("toastr");
37 |
38 | // // downgrading Angular 2 Components to Angular 1 Directives
39 | // angular.module('app').directive('profile',
40 | // upgradeAdapter.downgradeNg2Component(ProfileComponent));
41 | // angular.module('app').directive('unreviewedTalk',
42 | // upgradeAdapter.downgradeNg2Component(UnreviewedTalkComponent));
43 | // angular.module('app').directive('createNewSession',
44 | // upgradeAdapter.downgradeNg2Component(CreateNewSessionComponent));
45 | // angular.module('app').directive('detailPanel',
46 | // upgradeAdapter.downgradeNg2Component(DetailPanelComponent));
47 | // angular.module('app').directive('home',
48 | // upgradeAdapter.downgradeNg2Component(HomeComponent));
49 | // angular.module('app').directive('nav',
50 | // upgradeAdapter.downgradeNg2Component(NavComponent));
51 | // angular.module('app').directive('sessionDetailWithVotes',
52 | // upgradeAdapter.downgradeNg2Component(SessionDetailWithVotesComponent));
53 | // angular.module('app').directive('sessionDetail',
54 | // upgradeAdapter.downgradeNg2Component(SessionDetailComponent));
55 | // angular.module('app').directive('logout',
56 | // upgradeAdapter.downgradeNg2Component(LogoutComponent));
57 | // angular.module('app').directive('login',
58 | // upgradeAdapter.downgradeNg2Component(LoginComponent));
59 | // angular.module('app').directive('userList',
60 | // upgradeAdapter.downgradeNg2Component(UserListComponent));
61 | // angular.module('app').directive('userDetails',
62 | // upgradeAdapter.downgradeNg2Component(UserDetailsComponent));
63 | // angular.module('app').directive('results',
64 | // upgradeAdapter.downgradeNg2Component(ResultsComponent));
65 | // angular.module('app').directive('createUsers',
66 | // upgradeAdapter.downgradeNg2Component(CreateUsersComponent));
67 | // angular.module('app').directive('createUsers',
68 | // upgradeAdapter.downgradeNg2Component(CreateUsersComponent));
69 | // angular.module('app').directive('adminLogin',
70 | // upgradeAdapter.downgradeNg2Component(AdminLoginComponent));
71 | // }
--------------------------------------------------------------------------------
/public/userSessions.resolver.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Observable } from 'rxjs/Rx';
3 | import { ActivatedRouteSnapshot, RouterStateSnapshot, Resolve } from '@angular/router';
4 | import { Session } from './sessions/session.model';
5 | import { Sessions } from './sessions/sessions.service';
6 | import { CurrentIdentity } from './security/currentIdentity.service';
7 |
8 | @Injectable()
9 | export class SessionResolver implements Resolve {
10 |
11 | constructor(private sessions: Sessions,
12 | private currentIdentity: CurrentIdentity) {}
13 |
14 | resolve(
15 | route: ActivatedRouteSnapshot,
16 | state: RouterStateSnapshot
17 | ): Observable|Promise|any {
18 | console.log('hi there', route, state);
19 | var o = this.sessions.getSessionsByUser(this.currentIdentity.currentUser.id)
20 | o.subscribe(val => {
21 | console.log('val', val);
22 | })
23 | return o;
24 | }
25 | }
--------------------------------------------------------------------------------
/public/vendor/1.4.9/angular-route.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | AngularJS v1.4.9
3 | (c) 2010-2015 Google, Inc. http://angularjs.org
4 | License: MIT
5 | */
6 | (function(p,c,C){'use strict';function v(r,h,g){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(a,f,b,d,y){function z(){k&&(g.cancel(k),k=null);l&&(l.$destroy(),l=null);m&&(k=g.leave(m),k.then(function(){k=null}),m=null)}function x(){var b=r.current&&r.current.locals;if(c.isDefined(b&&b.$template)){var b=a.$new(),d=r.current;m=y(b,function(b){g.enter(b,null,m||f).then(function(){!c.isDefined(t)||t&&!a.$eval(t)||h()});z()});l=d.scope=b;l.$emit("$viewContentLoaded");
7 | l.$eval(w)}else z()}var l,m,k,t=b.autoscroll,w=b.onload||"";a.$on("$routeChangeSuccess",x);x()}}}function A(c,h,g){return{restrict:"ECA",priority:-400,link:function(a,f){var b=g.current,d=b.locals;f.html(d.$template);var y=c(f.contents());b.controller&&(d.$scope=a,d=h(b.controller,d),b.controllerAs&&(a[b.controllerAs]=d),f.data("$ngControllerController",d),f.children().data("$ngControllerController",d));y(a)}}}p=c.module("ngRoute",["ng"]).provider("$route",function(){function r(a,f){return c.extend(Object.create(a),
8 | f)}function h(a,c){var b=c.caseInsensitiveMatch,d={originalPath:a,regexp:a},g=d.keys=[];a=a.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?\*])?/g,function(a,c,b,d){a="?"===d?d:null;d="*"===d?d:null;g.push({name:b,optional:!!a});c=c||"";return""+(a?"":c)+"(?:"+(a?c:"")+(d&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");d.regexp=new RegExp("^"+a+"$",b?"i":"");return d}var g={};this.when=function(a,f){var b=c.copy(f);c.isUndefined(b.reloadOnSearch)&&(b.reloadOnSearch=!0);
9 | c.isUndefined(b.caseInsensitiveMatch)&&(b.caseInsensitiveMatch=this.caseInsensitiveMatch);g[a]=c.extend(b,a&&h(a,b));if(a){var d="/"==a[a.length-1]?a.substr(0,a.length-1):a+"/";g[d]=c.extend({redirectTo:a},h(d,b))}return this};this.caseInsensitiveMatch=!1;this.otherwise=function(a){"string"===typeof a&&(a={redirectTo:a});this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$templateRequest","$sce",function(a,f,b,d,h,p,x){function l(b){var e=s.current;
10 | (v=(n=k())&&e&&n.$$route===e.$$route&&c.equals(n.pathParams,e.pathParams)&&!n.reloadOnSearch&&!w)||!e&&!n||a.$broadcast("$routeChangeStart",n,e).defaultPrevented&&b&&b.preventDefault()}function m(){var u=s.current,e=n;if(v)u.params=e.params,c.copy(u.params,b),a.$broadcast("$routeUpdate",u);else if(e||u)w=!1,(s.current=e)&&e.redirectTo&&(c.isString(e.redirectTo)?f.path(t(e.redirectTo,e.params)).search(e.params).replace():f.url(e.redirectTo(e.pathParams,f.path(),f.search())).replace()),d.when(e).then(function(){if(e){var a=
11 | c.extend({},e.resolve),b,f;c.forEach(a,function(b,e){a[e]=c.isString(b)?h.get(b):h.invoke(b,null,null,e)});c.isDefined(b=e.template)?c.isFunction(b)&&(b=b(e.params)):c.isDefined(f=e.templateUrl)&&(c.isFunction(f)&&(f=f(e.params)),c.isDefined(f)&&(e.loadedTemplateUrl=x.valueOf(f),b=p(f)));c.isDefined(b)&&(a.$template=b);return d.all(a)}}).then(function(f){e==s.current&&(e&&(e.locals=f,c.copy(e.params,b)),a.$broadcast("$routeChangeSuccess",e,u))},function(b){e==s.current&&a.$broadcast("$routeChangeError",
12 | e,u,b)})}function k(){var a,b;c.forEach(g,function(d,g){var q;if(q=!b){var h=f.path();q=d.keys;var l={};if(d.regexp)if(h=d.regexp.exec(h)){for(var k=1,m=h.length;ka?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isPlainObject:function(a){var b;if("object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;if(a.constructor&&!k.call(a,"constructor")&&!k.call(a.constructor.prototype||{},"isPrototypeOf"))return!1;for(b in a);return void 0===b||k.call(a,b)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=d.createElement("script"),b.text=a,d.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:h.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(d=e.call(arguments,2),f=function(){return a.apply(b||this,d.concat(e.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new RegExp("^"+L+"*,"+L+"*"),S=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),T=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),U=new RegExp(O),V=new RegExp("^"+M+"$"),W={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,$=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_=/[+~]/,aa=/'|\\/g,ba=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ca=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},da=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(ea){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fa(a,b,d,e){var f,h,j,k,l,o,r,s,w=b&&b.ownerDocument,x=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==x&&9!==x&&11!==x)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==x&&(o=$.exec(a)))if(f=o[1]){if(9===x){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(w&&(j=w.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(o[2])return H.apply(d,b.getElementsByTagName(a)),d;if((f=o[3])&&c.getElementsByClassName&&b.getElementsByClassName)return H.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==x)w=b,s=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(aa,"\\$&"):b.setAttribute("id",k=u),r=g(a),h=r.length,l=V.test(k)?"#"+k:"[id='"+k+"']";while(h--)r[h]=l+" "+qa(r[h]);s=r.join(","),w=_.test(a)&&oa(b.parentNode)||b}if(s)try{return H.apply(d,w.querySelectorAll(s)),d}catch(y){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(Q,"$1"),b,d,e)}function ga(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ha(a){return a[u]=!0,a}function ia(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ja(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function ka(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function la(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function na(a){return ha(function(b){return b=+b,ha(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function oa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=fa.support={},f=fa.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fa.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ia(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ia(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Z.test(n.getElementsByClassName),c.getById=ia(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Z.test(n.querySelectorAll))&&(ia(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ia(function(a){var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Z.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ia(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",O)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Z.test(o.compareDocumentPosition),t=b||Z.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return ka(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?ka(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},fa.matches=function(a,b){return fa(a,null,null,b)},fa.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(T,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fa(b,n,null,[a]).length>0},fa.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fa.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fa.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fa.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fa.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fa.selectors={cacheLength:50,createPseudo:ha,match:W,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ba,ca),a[3]=(a[3]||a[4]||a[5]||"").replace(ba,ca),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fa.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fa.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return W.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&U.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ba,ca).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fa.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(P," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fa.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ha(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ha(function(a){var b=[],c=[],d=h(a.replace(Q,"$1"));return d[u]?ha(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ha(function(a){return function(b){return fa(a,b).length>0}}),contains:ha(function(a){return a=a.replace(ba,ca),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ha(function(a){return V.test(a||"")||fa.error("unsupported lang: "+a),a=a.replace(ba,ca).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Y.test(a.nodeName)},input:function(a){return X.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:na(function(){return[0]}),last:na(function(a,b){return[b-1]}),eq:na(function(a,b,c){return[0>c?c+b:c]}),even:na(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:na(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:na(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:na(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function ra(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j,k=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(j=b[u]||(b[u]={}),i=j[b.uniqueID]||(j[b.uniqueID]={}),(h=i[d])&&h[0]===w&&h[1]===f)return k[2]=h[2];if(i[d]=k,k[2]=a(b,c,g))return!0}}}function sa(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ta(a,b,c){for(var d=0,e=b.length;e>d;d++)fa(a,b[d],c);return c}function ua(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function va(a,b,c,d,e,f){return d&&!d[u]&&(d=va(d)),e&&!e[u]&&(e=va(e,f)),ha(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ta(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ua(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ua(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ua(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function wa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ra(function(a){return a===b},h,!0),l=ra(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ra(sa(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return va(i>1&&sa(m),i>1&&qa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(Q,"$1"),c,e>i&&wa(a.slice(i,e)),f>e&&wa(a=a.slice(e)),f>e&&qa(a))}m.push(c)}return sa(m)}function xa(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=F.call(i));u=ua(u)}H.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&fa.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ha(f):f}return h=fa.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xa(e,d)),f.selector=a}return f},i=fa.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ba,ca),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=W.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ba,ca),_.test(j[0].type)&&oa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qa(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||_.test(a)&&oa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ia(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ia(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ja("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ia(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ja("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ia(function(a){return null==a.getAttribute("disabled")})||ja(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fa}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.uniqueSort=n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},v=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},w=n.expr.match.needsContext,x=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,y=/^.[^:#\[\.,]*$/;function z(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(y.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return h.call(b,a)>-1!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(z(this,a||[],!1))},not:function(a){return this.pushStack(z(this,a||[],!0))},is:function(a){return!!z(this,"string"==typeof a&&w.test(a)?n(a):a||[],!1).length}});var A,B=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=n.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||A,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:B.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),x.test(e[1])&&n.isPlainObject(b))for(e in b)n.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&f.parentNode&&(this.length=1,this[0]=f),this.context=d,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?void 0!==c.ready?c.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};C.prototype=n.fn,A=n(d);var D=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=w.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?h.call(n(a),this[0]):h.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.uniqueSort(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function F(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return u(a,"parentNode")},parentsUntil:function(a,b,c){return u(a,"parentNode",c)},next:function(a){return F(a,"nextSibling")},prev:function(a){return F(a,"previousSibling")},nextAll:function(a){return u(a,"nextSibling")},prevAll:function(a){return u(a,"previousSibling")},nextUntil:function(a,b,c){return u(a,"nextSibling",c)},prevUntil:function(a,b,c){return u(a,"previousSibling",c)},siblings:function(a){return v((a.parentNode||{}).firstChild,a)},children:function(a){return v(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(E[a]||n.uniqueSort(e),D.test(a)&&e.reverse()),this.pushStack(e)}});var G=/\S+/g;function H(a){var b={};return n.each(a.match(G)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?H(a):n.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?n.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().progress(c.notify).done(c.resolve).fail(c.reject):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=e.call(arguments),d=c.length,f=1!==d||a&&n.isFunction(a.promise)?d:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?e.call(arguments):d,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(d>1)for(i=new Array(d),j=new Array(d),k=new Array(d);d>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().progress(h(b,j,i)).done(h(b,k,c)).fail(g.reject):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(d,[n]),n.fn.triggerHandler&&(n(d).triggerHandler("ready"),n(d).off("ready"))))}});function J(){d.removeEventListener("DOMContentLoaded",J),a.removeEventListener("load",J),n.ready()}n.ready.promise=function(b){return I||(I=n.Deferred(),"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(n.ready):(d.addEventListener("DOMContentLoaded",J),a.addEventListener("load",J))),I.promise(b)},n.ready.promise();var K=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)K(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},L=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function M(){this.expando=n.expando+M.uid++}M.uid=1,M.prototype={register:function(a,b){var c=b||{};return a.nodeType?a[this.expando]=c:Object.defineProperty(a,this.expando,{value:c,writable:!0,configurable:!0}),a[this.expando]},cache:function(a){if(!L(a))return{};var b=a[this.expando];return b||(b={},L(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[b]=c;else for(d in b)e[d]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=a[this.expando];if(void 0!==f){if(void 0===b)this.register(a);else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in f?d=[b,e]:(d=e,d=d in f?[d]:d.match(G)||[])),c=d.length;while(c--)delete f[d[c]]}(void 0===b||n.isEmptyObject(f))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!n.isEmptyObject(b)}};var N=new M,O=new M,P=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Q=/[A-Z]/g;function R(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Q,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:P.test(c)?n.parseJSON(c):c;
3 | }catch(e){}O.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return O.hasData(a)||N.hasData(a)},data:function(a,b,c){return O.access(a,b,c)},removeData:function(a,b){O.remove(a,b)},_data:function(a,b,c){return N.access(a,b,c)},_removeData:function(a,b){N.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=O.get(f),1===f.nodeType&&!N.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),R(f,d,e[d])));N.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){O.set(this,a)}):K(this,function(b){var c,d;if(f&&void 0===b){if(c=O.get(f,a)||O.get(f,a.replace(Q,"-$&").toLowerCase()),void 0!==c)return c;if(d=n.camelCase(a),c=O.get(f,d),void 0!==c)return c;if(c=R(f,d,void 0),void 0!==c)return c}else d=n.camelCase(a),this.each(function(){var c=O.get(this,d);O.set(this,d,b),a.indexOf("-")>-1&&void 0!==c&&O.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){O.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=N.get(a,b),c&&(!d||n.isArray(c)?d=N.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return N.get(a,c)||N.access(a,c,{empty:n.Callbacks("once memory").add(function(){N.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length",""],thead:[1,""],col:[2,""],tr:[2,""],td:[3,""],_default:[0,"",""]};$.optgroup=$.option,$.tbody=$.tfoot=$.colgroup=$.caption=$.thead,$.th=$.td;function _(a,b){var c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function aa(a,b){for(var c=0,d=a.length;d>c;c++)N.set(a[c],"globalEval",!b||N.get(b[c],"globalEval"))}var ba=/<|?\w+;/;function ca(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],o=0,p=a.length;p>o;o++)if(f=a[o],f||0===f)if("object"===n.type(f))n.merge(m,f.nodeType?[f]:f);else if(ba.test(f)){g=g||l.appendChild(b.createElement("div")),h=(Y.exec(f)||["",""])[1].toLowerCase(),i=$[h]||$._default,g.innerHTML=i[1]+n.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;n.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",o=0;while(f=m[o++])if(d&&n.inArray(f,d)>-1)e&&e.push(f);else if(j=n.contains(f.ownerDocument,f),g=_(l.appendChild(f),"script"),j&&aa(g),c){k=0;while(f=g[k++])Z.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),l.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",l.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var da=/^key/,ea=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,fa=/^([^.]*)(?:\.(.+)|)/;function ga(){return!0}function ha(){return!1}function ia(){try{return d.activeElement}catch(a){}}function ja(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ja(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=ha;else if(!e)return a;return 1===f&&(g=e,e=function(a){return n().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=n.guid++)),a.each(function(){n.event.add(this,b,e,d,c)})}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=N.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return"undefined"!=typeof n&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(G)||[""],j=b.length;while(j--)h=fa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=N.hasData(a)&&N.get(a);if(r&&(i=r.events)){b=(b||"").match(G)||[""],j=b.length;while(j--)if(h=fa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&N.remove(a,"handle events")}},dispatch:function(a){a=n.event.fix(a);var b,c,d,f,g,h=[],i=e.call(arguments),j=(N.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())a.rnamespace&&!a.rnamespace.test(g.namespace)||(a.handleObj=g,a.data=g.data,d=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==d&&(a.result=d)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&("click"!==a.type||isNaN(a.button)||a.button<1))for(;i!==this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>-1:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,la=/