Commit f60d5698a796fd0bd27f044887940247a4140139

Authored by Rohan Rangray

Fixed merge conflict in SettingsController.js

Showing 15 changed files Inline Diff

*~ 1 1 *~
venv* 2 2 venv*
static* 3
*.pyc 4 3 *.pyc
.idea* 5 4 .idea*
.*.swp 6 5 .*.swp
.*.swo 7 6 .*.swo
.*.swn 8 7 .*.swn
*.sqlite3 9 8 *.sqlite3
secrets 10 9 secrets
.coverage 11 10 .coverage
htmlcov* 12 11 htmlcov*
sass/*.css 13 12 sass/*.css
all: styles/materialize.css styles/flashier.css fixjsstyle 1 1 all: styles/materialize.css styles/flashier.css fixjsstyle
2 2
3 JS_SOURCES = config.js $(wildcard scripts/*Controller.js) $(wildcard scripts/*Directive.js) $(wildcard scripts/*Service.js)
4
5 print-%:
6 @echo '$*=$($*)'
7
styles/materialize.css: .PHONY 3 8 styles/materialize.css: .PHONY
sassc sass/materialize.scss styles/materialize.css 4 9 sassc sass/materialize.scss styles/materialize.css
5 10
styles/flashier.css: sass/flashier.scss 6 11 styles/flashier.css: sass/flashier.scss
sassc sass/flashier.scss styles/flashier.css 7 12 sassc sass/flashier.scss styles/flashier.css
8 13
fixjsstyle: .PHONY 9 14 fixjsstyle: .PHONY
casper_test.js View file @ f60d569
phantom.casperPath = 'path/to/node_modules/casperjs'; 1 File was deleted
phantom.injectJs('path/to/node_modules/casperjs/bin/bootstrap.js'); 2
3
phantom.casperTest = true; 4
5
6
//var casper = require('casper'); 7
var x = require('casper').selectXPath; 8
9
casper.start('http://google.com/', function() { 10
angular.module('flashy', [ 1 1 angular.module('flashy', [
'flashy.LoginController', 2 2 'flashy.LoginController',
'flashy.RootController', 3 3 'flashy.RootController',
'flashy.FeedController', 4 4 'flashy.FeedController',
'flashy.DeckController', 5 5 'flashy.DeckController',
'flashy.ClassAddController', 6 6 'flashy.ClassAddController',
'flashy.ClassDropController', 7 7 'flashy.ClassDropController',
'flashy.RequestResetController', 8 8 'flashy.RequestResetController',
'flashy.StudyController', 9 9 'flashy.StudyController',
'flashy.UserService', 10 10 'flashy.UserService',
'flashy.FlashcardDirective', 11 11 'flashy.FlashcardDirective',
'flashy.ResetPasswordController', 12 12 'flashy.ResetPasswordController',
'flashy.VerifyEmailController', 13 13 'flashy.VerifyEmailController',
'flashy.CardListController', 14 14 'flashy.CardListController',
'flashy.HelpController', 15 15 'flashy.HelpController',
'flashy.SettingsController', 16 16 'flashy.SettingsController',
'ngCookies']). 17 17 'ngCookies']).
config(function($stateProvider, $urlRouterProvider, $resourceProvider, $httpProvider, $locationProvider) { 18 18 config(function($stateProvider, $urlRouterProvider, $resourceProvider, $httpProvider, $locationProvider) {
'use strict'; 19 19 'use strict';
$httpProvider.defaults.withCredentials = true; 20 20 $httpProvider.defaults.withCredentials = true;
$httpProvider.defaults.xsrfCookieName = 'csrftoken'; 21 21 $httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken'; 22 22 $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
$resourceProvider.defaults.stripTrailingSlashes = false; 23 23 $resourceProvider.defaults.stripTrailingSlashes = false;
var arrayMethods = Object.getOwnPropertyNames(Array.prototype); 24 24 var arrayMethods = Object.getOwnPropertyNames(Array.prototype);
arrayMethods.forEach(attachArrayMethodsToNodeList); 25 25 arrayMethods.forEach(attachArrayMethodsToNodeList);
function attachArrayMethodsToNodeList(methodName) { 26 26 function attachArrayMethodsToNodeList(methodName) {
if (methodName !== 'length') { 27 27 if (methodName !== 'length') {
NodeList.prototype[methodName] = Array.prototype[methodName]; 28 28 NodeList.prototype[methodName] = Array.prototype[methodName];
} 29 29 }
} 30 30 }
31 31
$httpProvider.interceptors.push(function($q, $rootScope) { 32 32 $httpProvider.interceptors.push(function($q, $rootScope) {
return { 33 33 return {
'responseError': function(rejection) { // need a better redirect 34 34 'responseError': function(rejection) { // need a better redirect
if (rejection.status >= 500) { 35 35 if (rejection.status >= 500) {
console.log('got error'); 36 36 console.log('got error');
console.log(rejection); 37 37 console.log(rejection);
$rootScope.$broadcast('server_error', rejection); 38 38 $rootScope.$broadcast('server_error', rejection);
} 39 39 }
if (rejection.status == 403) { 40 40 if (rejection.status == 403) {
console.log(rejection); 41 41 console.log(rejection);
if (rejection.data && rejection.data.detail == 'Please verify your email before continuing') { 42 42 if (rejection.data && rejection.data.detail == 'Please verify your email before continuing') {
UserService.showLockedMessage(); 43 43 UserService.showLockedMessage();
UserService.logout(); 44 44 UserService.logout();
} 45 45 }
} 46 46 }
return $q.reject(rejection); 47 47 return $q.reject(rejection);
} 48 48 }
}; 49 49 };
}); 50 50 });
$locationProvider.html5Mode(true); 51 51 $locationProvider.html5Mode(true);
$urlRouterProvider.otherwise('/404'); 52 52 $urlRouterProvider.otherwise('/404');
var auth_resolve = { 53 53 var auth_resolve = {
authorize: function($q, $rootScope, $state, $stateParams, UserService) { 54 54 authorize: function($q, $rootScope, $state, $stateParams, UserService) {
55
console.log('do we need to authorize a user for', $rootScope.nextState.name); 56 55 console.log('do we need to authorize a user for', $rootScope.nextState.name);
if (UserService.noAuthRequired($rootScope.nextState)) { 57 56 if (UserService.noAuthRequired($rootScope.nextState)) {
console.log('no auth required for', $rootScope.nextState.name); 58 57 console.log('no auth required for', $rootScope.nextState.name);
return UserService.getUserData(); 59 58 return UserService.getUserData();
} 60 59 }
console.log('resolving user before continuing to ' + $rootScope.nextState.name); 61 60 console.log('resolving user before continuing to ' + $rootScope.nextState.name);
var redirectAsNeeded = function() { 62 61 var redirectAsNeeded = function() {
if (!UserService.isLoggedIn()) { 63 62 if (!UserService.isLoggedIn()) {
console.log(UserService.getUserData()); 64 63 console.log(UserService.getUserData());
console.log('making the user log in'); 65 64 console.log('making the user log in');
$state.go('login'); 66 65 $state.go('login');
} 67 66 }
if (!UserService.authorizedFor($rootScope.nextState, $rootScope.nextStateParams)) { 68 67 if (!UserService.authorizedFor($rootScope.nextState, $rootScope.nextStateParams)) {
console.log('user not authorized for ' + $rootScope.nextState.name); 69 68 console.log('user not authorized for ' + $rootScope.nextState.name);
$state.go('addclass'); 70 69 $state.go('addclass');
} 71 70 }
}; 72 71 };
if (UserService.isResolved()) return redirectAsNeeded(); 73 72 if (UserService.isResolved()) return redirectAsNeeded();
return UserService.getUserData().then(redirectAsNeeded); 74 73 return UserService.getUserData().then(redirectAsNeeded);
} 75 74 }
}; 76 75 };
$stateProvider. 77 76 $stateProvider.
state('login', { 78 77 state('login', {
resolve: auth_resolve, 79 78 resolve: auth_resolve,
url: '/login', 80 79 url: '/login',
templateUrl: 'templates/login.html', 81 80 templateUrl: 'templates/login.html',
controller: 'LoginController' 82 81 controller: 'LoginController'
}). 83 82 }).
state('root', { 84 83 state('root', {
resolve: auth_resolve, 85 84 resolve: auth_resolve,
url: '', 86 85 url: '',
controller: 'RootController' 87 86 controller: 'RootController'
}). 88 87 }).
state('feed', { 89 88 state('feed', {
resolve: auth_resolve, 90 89 resolve: auth_resolve,
url: '/feed/{sectionId}', 91 90 url: '/feed/{sectionId}',
templateUrl: 'templates/feed.html', 92 91 templateUrl: 'templates/feed.html',
controller: 'FeedController' 93 92 controller: 'FeedController'
}). 94 93 }).
state('cardlist', { 95 94 state('cardlist', {
resolve: auth_resolve, 96 95 resolve: auth_resolve,
url: '/cards/{sectionId}', 97 96 url: '/cards/{sectionId}',
templateUrl: 'templates/cardlist.html', 98 97 templateUrl: 'templates/cardlist.html',
controller: 'CardListController' 99 98 controller: 'CardListController'
}). 100 99 }).
state('addclass', { 101 100 state('addclass', {
resolve: auth_resolve, 102 101 resolve: auth_resolve,
url: '/addclass', 103 102 url: '/addclass',
templateUrl: 'templates/addclass.html', 104 103 templateUrl: 'templates/addclass.html',
controller: 'ClassAddController' 105 104 controller: 'ClassAddController'
}). 106 105 }).
state('dropclass', { 107 106 state('dropclass', {
resolve: auth_resolve, 108 107 resolve: auth_resolve,
url: '/settings/dropclass', 109 108 url: '/settings/dropclass',
templateUrl: 'templates/dropclass.html', 110 109 templateUrl: 'templates/dropclass.html',
controller: 'ClassDropController' 111 110 controller: 'ClassDropController'
}). 112 111 }).
state('deck', { 113 112 state('deck', {
resolve: auth_resolve, 114 113 resolve: auth_resolve,
url: '/deck/{sectionId}', 115 114 url: '/deck/{sectionId}',
templateUrl: 'templates/deck.html', 116 115 templateUrl: 'templates/deck.html',
controller: 'DeckController' 117 116 controller: 'DeckController'
}). 118 117 }).
state('study', { 119 118 state('study', {
resolve: auth_resolve, 120 119 resolve: auth_resolve,
url: '/study', 121 120 url: '/study',
templateUrl: 'templates/study.html', 122 121 templateUrl: 'templates/study.html',
controller: 'StudyController' 123 122 controller: 'StudyController'
}). 124 123 }).
state('flashcard', { 125 124 state('flashcard', {
resolve: auth_resolve, 126 125 resolve: auth_resolve,
url: '/flashcard', 127 126 url: '/flashcard',
templateUrl: 'templates/flashcard.html', 128 127 templateUrl: 'templates/flashcard.html',
controller: 'FlashcardController' 129 128 controller: 'FlashcardController'
}). 130 129 }).
state('settings', { 131 130 state('settings', {
resolve: auth_resolve, 132 131 resolve: auth_resolve,
url: '/settings', 133 132 url: '/settings',
templateUrl: 'templates/settings.html', 134 133 templateUrl: 'templates/settings.html',
controller: 'SettingsController' 135 134 controller: 'SettingsController'
}). 136 135 }).
state('requestpasswordreset', { 137 136 state('requestpasswordreset', {
url: '/requestpasswordreset', 138 137 url: '/requestpasswordreset',
templateUrl: 'templates/requestpasswordreset.html', 139 138 templateUrl: 'templates/requestpasswordreset.html',
controller: 'RequestResetController' 140 139 controller: 'RequestResetController'
}). 141 140 }).
state('resetpassword', { 142 141 state('resetpassword', {
url: '/resetpassword/{uid}/{token}', 143 142 url: '/resetpassword/{uid}/{token}',
templateUrl: 'templates/resetpassword.html', 144 143 templateUrl: 'templates/resetpassword.html',
controller: 'ResetPasswordController' 145 144 controller: 'ResetPasswordController'
}). 146 145 }).
state('verifyemail', { 147 146 state('verifyemail', {
url: '/verifyemail/{key}', 148 147 url: '/verifyemail/{key}',
templateUrl: 'templates/verifyemail.html', 149 148 templateUrl: 'templates/verifyemail.html',
controller: 'VerifyEmailController' 150 149 controller: 'VerifyEmailController'
}). 151 150 }).
state('404', { 152 151 state('404', {
url: '/404', 153 152 url: '/404',
template: "<h1>This page doesn't exist!</h1>" 154 153 template: "<h1>This page doesn't exist!</h1>"
}). 155 154 }).
state('help', { 156 155 state('help', {
resolve: auth_resolve, 157 156 resolve: auth_resolve,
url: '/help', 158 157 url: '/help',
templateUrl: 'templates/help.html', 159 158 templateUrl: 'templates/help.html',
controller: 'HelpController' 160 159 controller: 'HelpController'
}); 161 160 });
}). 162 161 }).
run(function($rootScope, $state, $stateParams, $location, UserService) { 163 162 run(function($rootScope, $state, $stateParams, $location, UserService) {
$rootScope.$on('$stateChangeError', function(event, toState, toParams, fromState, fromParams, error) { 164 163 $rootScope.$on('$stateChangeError', function(event, toState, toParams, fromState, fromParams, error) {
console.log('failed to change state: ' + error); 165 164 console.log('failed to change state: ' + error);
$state.go('login'); 166 165 $state.go('login');
}); 167 166 });
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) { 168 167 $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
$rootScope.nextState = toState; 169 168 $rootScope.nextState = toState;
$rootScope.nextStateParams = toParams; 170 169 $rootScope.nextStateParams = toParams;
console.log('changing state to', toState); 171 170 console.log('changing state to', toState);
if (['feed', 'deck', 'cardlist'].indexOf(toState.name) >= 0) { 172 171 if (['feed', 'deck', 'cardlist'].indexOf(toState.name) >= 0) {
localStorage.setItem('last_state', toState.name); 173 172 localStorage.setItem('last_state', toState.name);
localStorage.setItem('last_state_params', JSON.stringify(toParams)); 174 173 localStorage.setItem('last_state_params', JSON.stringify(toParams));
} 175 174 }
<!DOCTYPE html> 1 1 <!DOCTYPE html>
<html ng-app="flashy"> 2 2 <html ng-app="flashy">
<base href="/app/"> 3 3 <base href="/app/">
<head> 4 4 <head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/> 5 5 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<link rel="stylesheet" 6 6 <link rel="stylesheet"
href="https://ajax.googleapis.com/ajax/libs/angular_material/0.9.0/angular-material.min.css"> 7 7 href="https://ajax.googleapis.com/ajax/libs/angular_material/0.9.0/angular-material.min.css">
<link rel="shortcut icon" href="flashy.ico"> 8 8 <link rel="shortcut icon" href="flashy.ico">
9 9
<link rel="stylesheet" href="styles/flashier.css"/> 10 10 <link rel="stylesheet" href="styles/flashier.css"/>
<link rel="stylesheet" href="styles/flashy.css"/> 11 11 <link rel="stylesheet" href="styles/flashy.css"/>
<link rel="manifest" href="manifest.json"> 12 12 <link rel="manifest" href="manifest.json">
<link 13 13 <link
href='https://fonts.googleapis.com/css?family=Satisfy|Titillium+Web:400,200,200italic,300,600,700,900,700italic,600italic,400italic,300italic' 14 14 href='https://fonts.googleapis.com/css?family=Satisfy|Titillium+Web:400,200,200italic,300,600,700,900,700italic,600italic,400italic,300italic'
rel='stylesheet' type='text/css'> 15 15 rel='stylesheet' type='text/css'>
<title>Flashy</title> 16 16 <title>Flashy</title>
</head> 17 17 </head>
<body ng-controller="RootController"> 18 18 <body ng-controller="RootController">
<header> 19 19 <header>
<nav> 20 20 <nav>
<div class="nav-wrapper"> 21 21 <div class="nav-wrapper">
<a ng-show="UserService.isLoggedIn()" href="#" data-activates="mobile-demo" 22 22 <a ng-show="UserService.isLoggedIn()" href="#" data-activates="mobile-demo"
class="left button-collapse hide-on-med-and-up"><i 23 23 class="left button-collapse hide-on-med-and-up"><i
class="mdi-navigation-menu"></i></a> 24 24 class="mdi-navigation-menu"></i></a>
<!-- User's classes dropdown --> 25 25 <!-- User's classes dropdown -->
<ul id="classDropdown" class="dropdown-content"> 26 26 <ul id="classDropdown" class="dropdown-content">
<li ui-sref-active="active" ng-repeat="section in UserService.getUserData().sections"> 27 27 <li ui-sref-active="active" ng-repeat="section in UserService.getUserData().sections">
<a ui-sref="feed({sectionId:section.id})">{{section.short_name}}</a> 28 28 <a ui-sref="feed({sectionId:section.id})">{{section.short_name}}</a>
</li> 29 29 </li>
<li class="divider"></li> 30 30 <li class="divider"></li>
<li><a ui-sref="addclass">Add Class</a></li> 31 31 <li><a ui-sref="addclass">Add Class</a></li>
</ul> 32 32 </ul>
<ul ng-show="UserService.isLoggedIn()" class="left hide-on-small-and-down"> 33 33 <ul ng-show="UserService.isLoggedIn()" class="left hide-on-small-and-down">
<li><a style="font-size:20px; font-weight:700" class="dropdown-button ng-cloak hide-on-small-and-down" 34 34 <li><a style="font-size:20px; font-weight:700" class="dropdown-button ng-cloak hide-on-small-and-down"
href="#!" 35 35 href="#!"
data-activates="classDropdown">{{currentSection.id?currentSection.short_name:"Classes"}}<i 36 36 data-activates="classDropdown">{{currentSection.id?currentSection.short_name:"Classes"}}<i
class="mdi-navigation-arrow-drop-down right"></i></a></li> 37 37 class="mdi-navigation-arrow-drop-down right"></i></a></li>
<li ng-show="currentSection.id" ui-sref-active="active"><a ui-sref="feed({sectionId:currentSection.id})" 38 38 <li ng-show="currentSection.id" ui-sref-active="active"><a ui-sref="feed({sectionId:currentSection.id})"
class="tooltipped" 39 39 class="tooltipped"
data-position="bottom" 40 40 data-position="bottom"
data-delay="50" data-tooltip="Feed"><i 41 41 data-delay="50" data-tooltip="Feed"><i
class="mdi-action-view-module"></i></a></li> 42 42 class="mdi-action-view-module"></i></a></li>
<li ng-show="currentSection.id" ui-sref-active="active"><a ui-sref="deck({sectionId:currentSection.id})" 43 43 <li ng-show="currentSection.id" ui-sref-active="active"><a ui-sref="deck({sectionId:currentSection.id})"
class="tooltipped" 44 44 class="tooltipped"
data-position="bottom" 45 45 data-position="bottom"
data-delay="50" data-tooltip="Deck"><i 46 46 data-delay="50" data-tooltip="Deck"><i
class="mdi-action-view-carousel"></i></a></li> 47 47 class="mdi-action-view-carousel"></i></a></li>
<li ng-show="currentSection.id" ui-sref-active="active"><a ui-sref="cardlist({sectionId:currentSection.id})" 48 48 <li ng-show="currentSection.id" ui-sref-active="active"><a ui-sref="cardlist({sectionId:currentSection.id})"
class="tooltipped" 49 49 class="tooltipped"
data-position="bottom" 50 50 data-position="bottom"
data-delay="50" data-tooltip="Card List"><i 51 51 data-delay="50" data-tooltip="Card List"><i
class="mdi-action-view-list"></i></a></li> 52 52 class="mdi-action-view-list"></i></a></li>
</ul> 53 53 </ul>
<a href="#" class="brand-logo center">Flashy</a> 54 54 <a href="#" class="brand-logo center">Flashy</a>
55 55
<ul ng-show="UserService.isLoggedIn()" ng-cloak id="nav-mobile" class="right hide-on-small-and-down"> 56 56 <ul ng-show="UserService.isLoggedIn()" ng-cloak id="nav-mobile" class="right hide-on-small-and-down">
57 57
<li ui-sref-active="active"><a ui-sref="study" class="tooltipped" data-position="bottom" data-delay="50" 58 58 <li ui-sref-active="active"><a ui-sref="study" class="tooltipped" data-position="bottom" data-delay="50"
data-tooltip="Study"> 59 59 data-tooltip="Study">
<i class="tiny mdi-action-pageview"></i></a></li> 60 60 <i class="tiny mdi-action-pageview"></i></a></li>
61 61
<!-- Settings Dropdown --> 62 62 <!-- Settings Dropdown -->
<ul id="settingsDropdown" class="dropdown-content"> 63 63 <ul id="settingsDropdown" class="dropdown-content">
64 64
65 65
</ul> 66 66 </ul>
67 67
<li ui-sref-active="active"><a ui-sref="help"><i class="tiny mdi-action-help tooltipped" 68 68 <li ui-sref-active="active"><a ui-sref="help"><i class="tiny mdi-action-help tooltipped"
data-position="bottom" 69 69 data-position="bottom"
data-delay="50" data-tooltip="Help"></i></a></li> 70 70 data-delay="50" data-tooltip="Help"></i></a></li>
<li ui-sref-active="active"><a ui-sref="settings"><i data-position="bottom" data-delay="50" 71 71 <li ui-sref-active="active"><a ui-sref="settings"><i data-position="bottom" data-delay="50"
data-tooltip="Settings" 72 72 data-tooltip="Settings"
class="mdi-action-settings tooltipped"></i></a></li> 73 73 class="mdi-action-settings tooltipped"></i></a></li>
<li><a ng-click="logout()" ui-sref="login"><i data-position="bottom" data-delay="50" data-tooltip="Logout" 74 74 <li><a ng-click="logout()" ui-sref="login"><i data-position="bottom" data-delay="50" data-tooltip="Logout"
class="mdi-content-forward tooltipped"></i></a></li> 75 75 class="mdi-content-forward tooltipped"></i></a></li>
76 76
77 77
</ul> 78 78 </ul>
79 79
<!-- Slide-in side-nav for small screens --> 80 80 <!-- Slide-in side-nav for small screens -->
<ul ng-show="UserService.isLoggedIn()" class="side-nav" id="mobile-demo"> 81 81 <ul ng-show="UserService.isLoggedIn()" class="side-nav" id="mobile-demo">
<span ng-show="currentSection.id"> 82 82 <span ng-show="currentSection.id">
<li ui-sref-active="active"><a ui-sref="feed({sectionId:currentSection.id})" class="tooltipped"> 83 83 <li ui-sref-active="active"><a ui-sref="feed({sectionId:currentSection.id})" class="tooltipped">
<i class="mdi-action-view-module left"></i> 84 84 <i class="mdi-action-view-module left"></i>
Feed</a> 85 85 Feed</a>
</li> 86 86 </li>
<li ui-sref-active="active"><a ui-sref="deck({sectionId:currentSection.id})" class="tooltipped"> 87 87 <li ui-sref-active="active"><a ui-sref="deck({sectionId:currentSection.id})" class="tooltipped">
<i class="mdi-action-view-carousel left"> </i> 88 88 <i class="mdi-action-view-carousel left"> </i>
Deck 89 89 Deck
</a> 90 90 </a>
</li> 91 91 </li>
<li ui-sref-active="active"><a ui-sref="cardlist({sectionId:currentSection.id})" class="tooltipped"> 92 92 <li ui-sref-active="active"><a ui-sref="cardlist({sectionId:currentSection.id})" class="tooltipped">
<i class="mdi-action-view-list left"></i> 93 93 <i class="mdi-action-view-list left"></i>
Card List 94 94 Card List
</a> 95 95 </a>
</li> 96 96 </li>
<hr> 97 97 <hr>
</span> 98 98 </span>
<!-- Collapsible menu for all the User's classes --> 99 99 <!-- Collapsible menu for all the User's classes -->
<ul class="collapsible" data-collapsible="accordion"> 100 100 <ul class="collapsible" data-collapsible="accordion">
<li class="bold"> 101 101 <li class="bold">
<a class="collapsible-header black-text"> 102 102 <a class="collapsible-header black-text">
Classes 103 103 Classes
<i class="mdi-navigation-arrow-drop-down right"></i> 104 104 <i class="mdi-navigation-arrow-drop-down right"></i>
</a> 105 105 </a>
</li> 106 106 </li>
<div class="collapsible-body" style="display: block"> 107 107 <div class="collapsible-body" style="display: block">
<ul> 108 108 <ul>
<li ui-sref-active="active" ng-repeat="section in UserService.getUserData().sections"> 109 109 <li ui-sref-active="active" ng-repeat="section in UserService.getUserData().sections">
<a class="class bold" ui-sref="feed({sectionId:section.id})">{{section.short_name}}</a> 110 110 <a class="class bold" ui-sref="feed({sectionId:section.id})">{{section.short_name}}</a>
</li> 111 111 </li>
<hr> 112 112 <hr>
<li><a ui-sref="addclass"><i class="tiny mdi-content-add">Add Class</i></a></li> 113 113 <li><a ui-sref="addclass"><i class="tiny mdi-content-add">Add Class</i></a></li>
</ul> 114 114 </ul>
</div> 115 115 </div>
</ul> 116 116 </ul>
<li><a ui-sref="study">Study</a></li> 117 117 <li><a ui-sref="study">Study</a></li>
<li><a ui-sref="settings">Settings</a></li> 118 118 <li><a ui-sref="settings">Settings</a></li>
<li><a ng-click="logout()">Logout</a></li> 119 119 <li><a ng-click="logout()">Logout</a></li>
</ul> 120 120 </ul>
</div> 121 121 </div>
</nav> 122 122 </nav>
123 123
</header> 124 124 </header>
125 125
126 126
<!-- Menu Bar --> 127 127 <!-- Menu Bar -->
<main ui-view></main> 128 128 <main ui-view></main>
129 129
130 130
<!--<footer class="page-footer">--> 131 131 <!--<footer class="page-footer">-->
<!--<div class="footer-copyright">--> 132 132 <!--<div class="footer-copyright">-->
<!--<div class="container">--> 133 133 <!--<div class="container">-->
<!--&copy; 2015 Team Swag--> 134 134 <!--&copy; 2015 Team Swag-->
<!--<a class="grey-text text-lighten-4 right" id="contact" href="mailto:halp@flashy.cards">Concerns? Contact us by--> 135 135 <!--<a class="grey-text text-lighten-4 right" id="contact" href="mailto:halp@flashy.cards">Concerns? Contact us by-->
<!--email!</a>--> 136 136 <!--email!</a>-->
<!--</div>--> 137 137 <!--</div>-->
138 138
<!--</div>--> 139 139 <!--</div>-->
<!--</footer>--> 140 140 <!--</footer>-->
141 141
</body> 142 142 </body>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js"></script> 143 143 <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.14/angular-ui-router.js"></script> 144 144 <script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.14/angular-ui-router.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-cookies.js"></script> 145 145 <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-cookies.js"></script>
<script src="//code.jquery.com/jquery-2.1.4.min.js"></script> 146 146 <script src="//code.jquery.com/jquery-2.1.4.min.js"></script>
<script type="text/javascript" src="scripts/materialize.js"></script> 147 147 <script type="text/javascript" src="scripts/materialize.js"></script>
<script type="text/javascript" src="scripts/jquery.collapsible.js"></script> 148 148 <script type="text/javascript" src="scripts/jquery.collapsible.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angular_material/0.9.0/angular-material.min.js"></script> 149 149 <script src="https://ajax.googleapis.com/ajax/libs/angular_material/0.9.0/angular-material.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-animate.min.js"></script> 150 150 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-animate.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-aria.min.js"></script> 151 151 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-aria.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-resource.min.js"></script> 152 152 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-resource.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-sanitize.js"></script> 153 153 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-sanitize.js"></script>
<script src="https://cdn.rawgit.com/gdi2290/angular-websocket/v1.0.9/angular-websocket.min.js"></script> 154 154 <script src="static/js/angular-websocket.js"></script>
<script src="https://cdn.rawgit.com/akatov/angular-contenteditable/master/angular-contenteditable.js"></script> 155 155 <script src="static/js/angular-contenteditable.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.4/angular-filter.js"></script> 156 156 <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.4/angular-filter.js"></script>
157 157
158 158
<script src="config.js"></script> 159 159 <script src="config.js"></script>
160 160
<!-- Controllers --> 161 161 <!-- Controllers -->
<script src="scripts/FeedController.js"></script> 162 162 <script src="scripts/FeedController.js"></script>
<script src="scripts/RootController.js"></script> 163 163 <script src="scripts/RootController.js"></script>
<script src="scripts/SettingsController.js"></script> 164 164 <script src="scripts/SettingsController.js"></script>
scripts/CardGridController.js View file @ f60d569
angular.module('flashy.CardGridController', ['ui.router', 'ngAnimate', 'ngWebSocket']).controller = 1 1 angular.module('flashy.CardGridController', ['ui.router', 'ngAnimate', 'ngWebSocket']).controller =
function($scope, $rootScope, $state, $http, $window, $timeout, $stateParams, $websocket, UserService) { 2 2 function($scope, $rootScope, $state, $http, $window, $timeout, $stateParams, $websocket, UserService) {
$scope.cards = false; 3 3 $scope.cards = false;
$scope.cardCols = []; // organized data 4 4 $scope.cardCols = []; // organized data
$scope.numCols = 0; 5 5 $scope.numCols = 0;
$scope.cardTable = {}; // look up table of cards, {'colNum':col, 'obj':card} 6 6 $scope.cardTable = {}; // look up table of cards, {'colNum':col, 'obj':card}
$scope.sectionId = parseInt($stateParams.sectionId); 7 7 $scope.sectionId = parseInt($stateParams.sectionId);
$scope.section = $rootScope.SectionResource.get({sectionId: $scope.sectionId}); 8 8 $scope.section = $rootScope.SectionResource.get({sectionId: $scope.sectionId});
$rootScope.currentSection = $scope.section; 9 9 $rootScope.currentSection = $scope.section;
10 10
if (!UserService.isInSection($scope.sectionId)) { 11 11 if (!UserService.isInSection($scope.sectionId)) {
console.log('user is not enrolled in ' + $scope.sectionId); 12 12 console.log('user is not enrolled in ' + $scope.sectionId);
$state.go('addclass'); 13 13 $state.go('addclass');
} 14 14 }
15 15
$scope.refreshColumnWidth = function() { 16 16 $scope.refreshColumnWidth = function() {
avail = $window.innerWidth - 17; 17 17 avail = $window.innerWidth - 17;
width = Math.floor(avail / Math.floor(avail / 250)); 18 18 width = Math.floor(avail / Math.floor(avail / 250));
$('.cardColumn').css({ 19 19 $('.cardColumn').css({
width: width + 'px', 20 20 width: width + 'px',
'font-size': 100 * width / 250 + '%' 21 21 'font-size': 100 * width / 250 + '%'
}); 22 22 });
$('.cardColumn .card.flashy').css({ 23 23 $('.cardColumn .card.flashy').css({
width: width - 12 + 'px', 24 24 width: width - 12 + 'px',
height: (width * 3 / 5) + 'px' 25 25 height: (width * 3 / 5) + 'px'
}); 26 26 });
}; 27 27 };
28 28
$scope.pullCard = function(id) { 29 29 $scope.pullCard = function(id) {
if ($scope.deck_cards[id]) return console.log('Not pulling', id, "because it's already in deck"); 30 30 if ($scope.deck_cards[id]) return console.log('Not pulling', id, "because it's already in deck");
$http.post('/api/flashcards/' + id + '/pull/'). 31 31 $http.post('/api/flashcards/' + id + '/pull/').
success(function(data) { 32 32 success(function(data) {
console.log('pulled flashcard #' + id); 33 33 console.log('pulled flashcard #' + id);
}). 34 34 }).
error(function(data) { 35 35 error(function(data) {
console.log('failed to pull flashcard #' + id); 36 36 console.log('failed to pull flashcard #' + id);
}); 37 37 });
}; 38 38 };
$scope.unpullCard = function(id) { 39 39 $scope.unpullCard = function(id) {
if (!$scope.deck_cards[id]) return console.log('Not unpulling', id, "because it's not in deck"); 40 40 if (!$scope.deck_cards[id]) return console.log('Not unpulling', id, "because it's not in deck");
$http.post('/api/flashcards/' + id + '/unpull/'). 41 41 $http.post('/api/flashcards/' + id + '/unpull/').
success(function(data) { 42 42 success(function(data) {
console.log('unpulled flashcard #' + id); 43 43 console.log('unpulled flashcard #' + id);
}). 44 44 }).
error(function(data) { 45 45 error(function(data) {
console.log('failed to unpull flashcard #' + id); 46 46 console.log('failed to unpull flashcard #' + id);
}); 47 47 });
}; 48 48 };
$scope.refreshLayout = function() { 49 49 $scope.refreshLayout = function() {
numCols = Math.max(1, Math.floor(($window.innerWidth - 17) / 250)); 50 50 numCols = Math.max(1, Math.floor(($window.innerWidth - 17) / 250));
51 51
// check if we actually need to refresh the whole layout 52 52 // check if we actually need to refresh the whole layout
if (numCols == $scope.numCols) return $scope.refreshColumnWidth(); 53 53 if (numCols == $scope.numCols) return $scope.refreshColumnWidth();
$scope.numCols = numCols; 54 54 $scope.numCols = numCols;
console.log('refreshing layout for ' + $scope.numCols + ' columns'); 55 55 console.log('refreshing layout for ' + $scope.numCols + ' columns');
$scope.cardCols = []; 56 56 $scope.cardCols = [];
var cols = []; 57 57 var cols = [];
for (i = 0; i < $scope.numCols; i++) cols.push([]); 58 58 for (i = 0; i < $scope.numCols; i++) cols.push([]);
$scope.cards.forEach(function(card, i) { 59 59 $scope.cards.forEach(function(card, i) {
cols[i % $scope.numCols].push(card); 60 60 cols[i % $scope.numCols].push(card);
$scope.cardTable[card.id] = {'colNum': (i % $scope.numCols), 'obj': card}; 61 61 $scope.cardTable[card.id] = {'colNum': (i % $scope.numCols), 'obj': card};
card.isInDeck(); 62 62 card.isInDeck();
}); 63 63 });
// wait until the next digest cycle to update cardCols 64 64 // wait until the next digest cycle to update cardCols
console.log(cols); 65 65 console.log(cols);
$timeout(function() { 66 66 $timeout(function() {
$scope.cardCols = cols; 67 67 $scope.cardCols = cols;
$timeout($scope.refreshColumnWidth); 68 68 $timeout($scope.refreshColumnWidth);
}); 69 69 });
70 70
}; 71 71 };
72 72
angular.element($window).bind('resize', $scope.refreshLayout); 73 73 angular.element($window).bind('resize', $scope.refreshLayout);
74 74
$scope.ws_host = window.location.origin.replace('http', 'ws'); 75 75 $scope.ws_host = window.location.origin.replace('http', 'ws');
$scope.deck_ws = $websocket($scope.ws_host + '/ws/deck/' + $scope.sectionId + '?subscribe-user'); 76 76 $scope.deck_ws = $websocket($scope.ws_host + '/ws/deck/' + $scope.sectionId + '?subscribe-user');
$scope.deck_ws.onOpen(function() { 77 77 $scope.deck_ws.onOpen(function() {
console.log('deck ws open'); 78 78 console.log('deck ws open');
}); 79 79 });
80 80
$scope.deck_ws.onMessage(function(message) { 81 81 $scope.deck_ws.onMessage(function(message) {
data = JSON.parse(message.data); 82 82 data = JSON.parse(message.data);
console.log('message', data); 83 83 console.log('message', data);
if (data.event_type == 'pull_card') { 84 84 if (data.event_type == 'pull_card') {
card = data.flashcard; 85 85 card = data.flashcard;
console.log('pulling', card); 86 86 console.log('pulling', card);
$scope.deck_cards[card.id] = card; 87 87 $scope.deck_cards[card.id] = card;
} 88 88 }
if (data.event_type == 'unpull_card') { 89 89 if (data.event_type == 'unpull_card') {
card = data.flashcard; 90 90 card = data.flashcard;
$scope.deck_cards[card.id] = undefined; 91 91 $scope.deck_cards[card.id] = undefined;
} 92 92 }
}); 93 93 });
94 94
$scope.cardInDeck = function(id) { 95 95 $scope.cardInDeck = function(id) {
return $scope.deck_cards[id]; 96 96 return $scope.deck_cards[id];
}; 97 97 };
$scope.add = function(card) { 98 98 $scope.add = function(card) {
var colNum = 0; 99 99 var colNum = 0;
var lowestCol = $scope.cardCols[0]; 100 100 var lowestCol = $scope.cardCols[0];
var lowestColNum = 0; 101 101 var lowestColNum = 0;
while (colNum < $scope.numCols) { 102 102 while (colNum < $scope.numCols) {
if ($scope.cardCols[colNum].length == 0) { 103 103 if ($scope.cardCols[colNum].length == 0) {
lowestCol = $scope.cardCols[colNum]; 104 104 lowestCol = $scope.cardCols[colNum];
break; 105 105 break;
} else if ($scope.cardCols[colNum].length < lowestCol.length) { 106 106 } else if ($scope.cardCols[colNum].length < lowestCol.length) {
lowestCol = $scope.cardCols[colNum]; 107 107 lowestCol = $scope.cardCols[colNum];
lowestColNum = colNum; 108 108 lowestColNum = colNum;
lowestColLen = $scope.cardCols[colNum].length; 109 109 lowestColLen = $scope.cardCols[colNum].length;
} 110 110 }
colNum++; 111 111 colNum++;
} 112 112 }
console.log(card); 113 113 console.log(card);
$scope.cards.push(data); 114 114 $scope.cards.push(data);
lowestCol.unshift(card); 115 115 lowestCol.unshift(card);
$scope.cardTable[card.id] = {'colNum': lowestColNum, 'obj': card}; 116 116 $scope.cardTable[card.id] = {'colNum': lowestColNum, 'obj': card};
$timeout($scope.refreshColumnWidth); 117 117 $timeout($scope.refreshColumnWidth);
}; 118 118 };
119 119
120
121 $scope.deck_cards = [];
$http.get('/api/sections/' + $scope.sectionId + '/deck/'). 120 122 $http.get('/api/sections/' + $scope.sectionId + '/deck/').
success(function(data) { 121 123 success(function(data) {
$scope.deck_cards = []; 122 124
for (i in data) $scope.deck_cards[data[i].id] = data[i]; 123 125 for (i in data) $scope.deck_cards[data[i].id] = data[i];
console.log("got user's deck"); 124 126 console.log("got user's deck");
}). 125 127 }).
scripts/ResetPasswordController.js View file @ f60d569
angular.module('flashy.ResetPasswordController', ['ui.router']). 1 1 angular.module('flashy.ResetPasswordController', ['ui.router']).
2 2
controller('ResetPasswordController', ['$scope', '$state', '$http', '$timeout', 3 3 controller('ResetPasswordController', ['$scope', '$state', '$http', '$timeout',
function($scope, $state, $http, $timeout) { 4 4 function($scope, $state, $http, $timeout) {
'use strict'; 5 5 'use strict';
var url = document.location.href.split('/'); 6 6 var url = document.location.href.split('/');
var token = url[url.length - 1]; 7 7 var token = url[url.length - 1];
var uid = url[url.length - 2]; 8 8 var uid = url[url.length - 2];
$scope.error = false; 9 9 $scope.error = false;
$scope.success = false; 10 10 $scope.success = false;
$scope.mismatch = false; 11 11 $scope.mismatch = false;
$scope.unacceptable = false; 12 12 $scope.unacceptable = false;
/*if(token == 'resetpassword') { 13 13 /*if(token == 'resetpassword') {
$state.go('login'); 14 14 $state.go('login');
}*/ 15 15 }*/
console.log('RESETTING'); 16 16 console.log('RESETTING');
$scope.confirmResetPass = function() { 17 17 $scope.confirmResetPass = function() {
if ($scope.newPassword.length < 8) { 18 18 if ($scope.newPassword.length < 8) {
$scope.unacceptable = true; 19 19 $scope.unacceptable = true;
return; 20 20 return;
} 21 21 }
if ($scope.newPassword != $scope.confirmPassword) { 22 22 if ($scope.newPassword != $scope.confirmPassword) {
$scope.mismatch = true; 23 23 $scope.mismatch = true;
$scope.confirmPassword.$setPristine(); 24 24 $scope.confirmPassword.$setPristine();
console.log('mismatch'); 25 25 console.log('mismatch');
return; 26 26 return;
} 27 27 }
/*password passes local tests*/ 28 28 /*password passes local tests*/
$http.post('/api/reset_password/', JSON.stringify({ 29 29 $http.post('/api/reset_password/', JSON.stringify({
'uid': uid, 30 30 'uid': uid,
'token': token, 31 31 'token': token,
'new_password': $scope.newPassword 32 32 'new_password': $scope.newPassword
})) 33 33 }))
.success(function(data) { 34 34 .success(function(data) {
$scope.error = false; 35 35 $scope.error = false;
$scope.success = true; 36 36 $scope.success = true;
//$state.go('resetpasssuccess'); 37 37 //$state.go('resetpasssuccess');
$timeout(function($state) { 38 38 $timeout(function($state) {
$state.go('login'); 39 39 $state.go('login');
}, 1000); 40 40 }, 1000);
console.log(data); 41 41 console.log(data);
}) 42 42 })
.error(function(data, status, header, config) { 43 43 .error(function(data, status, header, config) {
$scope.error = true; 44 44 $scope.error = true;
$scope.success = false; 45 45 $scope.success = false;
$scope.mismatch = false; 46 46 $scope.mismatch = false;
$scope.unacceptable = false; 47 47 $scope.unacceptable = false;
console.log(data); 48 48 console.log(data);
}); 49 49 });
}; 50 50 };
$scope.cancelReset = function() { 51 51 $scope.cancelReset = function() {
$state.go('login'); 52 52 $state.go('login');
}; 53 53 };
}]); 54 54 }]);
55 55
scripts/SettingsController.js View file @ f60d569
angular.module('flashy.SettingsController', ['ui.router']). 1 1 angular.module('flashy.SettingsController', ['ui.router']).
2 2
controller('SettingsController', function($scope, $http) { 3 3 controller('SettingsController', function($scope, $http) {
$scope.changePassword = function(oldPassword, newPassword, confirmedNewPassword) { 4 4 $scope.changePassword = function(oldPassword, newPassword, confirmedNewPassword) {
5 5
6
7
8
}; 9 6 };
console.log("checking to see if chrome"); 10 7 console.log("checking to see if chrome");
if (!chrome) { return; } 11 8 if (!chrome) { return; }
console.log("chrome"); 12 9 console.log("chrome");
$scope.registerCallback = function(registrationId) { 13 10 $scope.registerCallback = function(registrationId) {
if (chrome.runtime.lastError) { 14 11 if (chrome.runtime.lastError) {
console.log("Registration failed") 15 12 console.log("Registration failed")
} 16 13 }
17 14
sendRegistrationId(registrationId, function(succeed) { 18 15 sendRegistrationId(registrationId, function(succeed) {
if (succeed) { 19 16 if (succeed) {
chrome.storage.local.set({registered: true}); 20 17 chrome.storage.local.set({registered: true});
} 21 18 }
}); 22 19 });
}; 23 20 };
24 21
function sendRegistrationId(registrationId, callback) { 25 22 function sendRegistrationId(registrationId, callback) {
console.log("registration id: "+registrationId); 26 23 console.log("registration id: "+registrationId);
$http.post('/api/subscribe/', JSON.stringify({ 27 24 $http.post('/api/subscribe/', JSON.stringify({
'registration_id': registrationId 28 25 'registration_id': registrationId
})); 29 26 }));
callback(true); 30 27 callback(true);
} 31 28 }
32 29
console.log(chrome.runtime.onStartup); 33 30 console.log(chrome.runtime.onStartup);
chrome.runtime.onStartup.addListener(function() { 34 31 chrome.runtime.onStartup.addListener(function() {
chrome.storage.local.get("registered", function(result) { 35 32 chrome.storage.local.get("registered", function(result) {
// If already registered, bail out. 36 33 // If already registered, bail out.
scripts/StudyController.js View file @ f60d569
angular.module('flashy.StudyController', ['ui.router']). 1 1 angular.module('flashy.StudyController', ['ui.router']).
2 2
controller('StudyController', ['$scope', '$stateParams', '$state', '$http', 'UserService', 3 3 controller('StudyController', ['$scope', '$stateParams', '$state', '$http', 'UserService',
function($scope, $stateParams, $state, $http, UserService) { 4 4 function($scope, $stateParams, $state, $http, UserService) {
console.log('Flashy study controller content in this file. also hell0'); 5 5 console.log('Flashy study controller content in this file. also hell0');
sectionId = $stateParams.sectionId; 6 6 sectionId = $stateParams.sectionId;
$scope.isParamOpen = true; 7 7 $scope.isParamOpen = true;
8 8
$(document).ready(function() { 9 9 $(document).ready(function() {
$('.datepicker').pickadate({ 10 10 $('.datepicker').pickadate({
selectMonths: true, // Creates a dropdown to control month 11 11 selectMonths: true, // Creates a dropdown to control month
selectYears: 15 // Creates a dropdown of 15 years to control year 12 12 selectYears: 15 // Creates a dropdown of 15 years to control year
}); 13 13 });
14 14
$('select').material_select(); 15 15 $('select').material_select();
}); 16 16 });
17 17
$scope.UserService = UserService; 18 18 $scope.UserService = UserService;
$scope.isParamOpen = true; 19 19 $scope.isParamOpen = true;
20 20
console.log($scope.UserService.getUserData().sections); 21 21 console.log($scope.UserService.getUserData().sections);
22 22
$scope.toggleSectionToStudy = function(id) { 23 23 $scope.toggleSectionToStudy = function(id) {
console.log('toggle sect', id); 24 24 console.log('toggle sect', id);
$scope.sectionToStudy = id; 25 25 $scope.sectionToStudy = id;
}; 26 26 };
27 27
$scope.openParams = function() { 28 28 $scope.openParams = function() {
$scope.isParamOpen = !$scope.isParamOpen; 29 29 $scope.isParamOpen = !$scope.isParamOpen;
}; 30 30 };
31 31
$scope.fetchQuiz = function(a, b) { 32 32 $scope.fetchQuiz = function(a, b) {
//console.log($scope.startDate, $scope.endDate); 33 33 //console.log($scope.startDate, $scope.endDate);
console.log(a, b); 34 34 console.log(a, b);
}; 35 35 };
36 36
/* 37 37 /*
$scope.fetchQuiz = function() { 38 38 $scope.fetchQuiz = function() {
console.log('fetching quiz...'); 39 39 console.log('fetching quiz...');
var studyRequest = { 40 40 var studyRequest = {
'sections': ($scope.sectionToStudy == null) ? [] : [$scope.sectionToStudy], 41 41 'sections': ($scope.sectionToStudy == null) ? [] : [$scope.sectionToStudy],
'material_date_begin':, 42 42 'material_date_begin':,
'material_date_end': 43 43 'material_date_end':
}; 44 44 };
45 45
// $http.post('/api/study/', studyRequest). 46 46 // $http.post('/api/study/', studyRequest).
//TODO 47 47 //TODO
}; 48 48 };
*/ 49 49 */
50 50
/* OLD STUFF :in case you still neeed it */ 51 51 /* OLD STUFF :in case you still neeed it */
// Flashcard content 52 52 // Flashcard content
$scope.htmlContent = 'sample text here longwordddddddddddddddddddddddddddd hello there from js review ctrl alwkejflakewjflk awjkefjkwefjlkea jfkewjaweajkakwef jk fjeawkafj kaewjf jawekfj akwejfk '; 53 53 $scope.htmlContent = 'sample text here longwordddddddddddddddddddddddddddd hello there from js review ctrl alwkejflakewjflk awjkefjkwefjlkea jfkewjaweajkakwef jk fjeawkafj kaewjf jawekfj akwejfk ';
//single card 54 54 //single card
$scope.samples = 55 55 $scope.samples =
{ 56 56 {
'name': 'lol', 57 57 'name': 'lol',
'text': 'sample text here 111111 woo hoo I think it works', 58 58 'text': 'sample text here 111111 woo hoo I think it works',
'mask': [[0, 6], [16, 23]] 59 59 'mask': [[0, 6], [16, 23]]
}; 60 60 };
61 61
// get text to display as array 62 62 // get text to display as array
$scope.displayText = []; 63 63 $scope.displayText = [];
// get answers to blanks as array 64 64 // get answers to blanks as array
$scope.blankText = []; 65 65 $scope.blankText = [];
var start = 0; // where to start next string break 66 66 var start = 0; // where to start next string break
for (var i = 0; i < $scope.samples.mask.length; i++) { 67 67 for (var i = 0; i < $scope.samples.mask.length; i++) {
$scope.displayText.push($scope.samples.text.substring(start, $scope.samples.mask[i][0])); 68 68 $scope.displayText.push($scope.samples.text.substring(start, $scope.samples.mask[i][0]));
$scope.blankText.push($scope.samples.text.substring($scope.samples.mask[i][0], $scope.samples.mask[i][1])); 69 69 $scope.blankText.push($scope.samples.text.substring($scope.samples.mask[i][0], $scope.samples.mask[i][1]));
start = $scope.samples.mask[i][1]; 70 70 start = $scope.samples.mask[i][1];
} 71 71 }
if (start != $scope.samples.mask.length - 1) 72 72 if (start != $scope.samples.mask.length - 1)
$scope.displayText.push($scope.samples.text.substring(start)); 73 73 $scope.displayText.push($scope.samples.text.substring(start));
scripts/UserService.js View file @ f60d569
angular.module('flashy.UserService', ['ui.router']). 1 1 angular.module('flashy.UserService', ['ui.router']).
service('UserService', function($rootScope, $http, $q) { 2 2 service('UserService', function($rootScope, $http, $q) {
var deferred = $q.defer(); 3 3 var deferred = $q.defer();
var _user = false; 4 4 var _user = false;
var login = function(data) { 5 5 var login = function(data) {
if (data.locked) { 6 6 if (data.locked) {
$rootScope.UserService.showLockedMessage(); 7 7 $rootScope.UserService.showLockedMessage();
return deferred.reject('account locked'); 8 8 return deferred.resolve(data);
} 9 9 }
if (!data.is_confirmed) { 10 10 if (!data.is_confirmed) {
Materialize.toast('Please verify your email address! ' + 11 11 Materialize.toast('Please verify your email address! ' +
'<a class="btn-flat cyan-text" onclick="rootscope.UserService.resendConfirmationEmail()">' + 12 12 '<a class="btn-flat cyan-text" onclick="rootscope.UserService.resendConfirmationEmail()">' +
'Resend Verification Email</a>', 4000); 13 13 'Resend Verification Email</a>', 4000);
} 14 14 }
_user = data; 15 15 _user = data;
_user.sectionIdList = _user.sections.map(function(x) { 16 16 _user.sectionIdList = _user.sections.map(function(x) {
return x.id; 17 17 return x.id;
}); 18 18 });
deferred.resolve(data); 19 19 deferred.resolve(data);
}; 20 20 };
this.login = login; 21 21 this.login = login;
$http.get('/api/me/').success(function(data) { 22 22 $http.get('/api/me/').success(function(data) {
console.log('user is logged in!'); 23 23 console.log('user is logged in!');
login(data); 24 24 login(data);
}).error(function(data) { 25 25 }).error(function(data) {
console.log(data); 26 26 console.log(data);
console.log('not logged in yet: ' + data.detail); 27 27 console.log('not logged in yet: ' + data.detail);
_user = {email: false}; 28 28 _user = {email: false};
deferred.resolve(_user); 29 29 deferred.resolve(_user);
}); 30 30 });
31 31
this.isResolved = function() { 32 32 this.isResolved = function() {
return !!_user; 33 33 return !!_user;
}; 34 34 };
this.getUserData = function() { 35 35 this.getUserData = function() {
if (this.isResolved()) return _user; 36 36 if (this.isResolved()) return _user;
else return deferred.promise; 37 37 else return deferred.promise;
}; 38 38 };
this.hasVerifiedEmail = function() { 39 39 this.hasVerifiedEmail = function() {
return this.isResolved() && _user.is_confirmed; 40 40 return this.isResolved() && _user.is_confirmed;
}; 41 41 };
42 42
this.logout = function($state) { 43 43 this.logout = function($state) {
$http.post('/api/logout/').success(function() { 44 44 $http.post('/api/logout/').success(function() {
if (!_user.locked)Materialize.toast('Logged out!', 1000); 45 45 if (!_user.locked)Materialize.toast('Logged out!', 1000);
}).error(function() { 46 46 }).error(function() {
console.log('Problem logging out'); 47 47 console.log('Problem logging out');
}); 48 48 });
_user = false; 49 49 _user = false;
deferred.resolve({}); 50 50 deferred.resolve({});
$state.go('login'); 51 51 $state.go('login');
}; 52 52 };
this.addClass = function(section) { 53 53 this.addClass = function(section) {
_user.sections.push(section); 54 54 _user.sections.push(section);
_user.sectionIdList.push(section.id); 55 55 _user.sectionIdList.push(section.id);
}; 56 56 };
this.isLoggedIn = function() { 57 57 this.isLoggedIn = function() {
rv = this.isResolved() && _user.email; 58 58 rv = this.isResolved() && _user.email;
return rv; 59 59 return rv;
}; 60 60 };
this.isInSection = function(sectionId) { 61 61 this.isInSection = function(sectionId) {
return (_user.sectionIdList.indexOf(sectionId) >= 0); 62 62 return (_user.sectionIdList.indexOf(sectionId) >= 0);
}; 63 63 };
this.redirectToDefaultState = function($state) { 64 64 this.redirectToDefaultState = function($state) {
console.log('redirecting user to their default state'); 65 65 console.log('redirecting user to their default state');
// if the user isn't logged in, log in! 66 66 // if the user isn't logged in, log in!
if (!this.isLoggedIn()) return $state.go('login'); 67 67 if (!this.isLoggedIn()) return $state.go('login');
// if the user isn't enrolled in any sections, go to addclass 68 68 // if the user isn't enrolled in any sections, go to addclass
if (!_user.sections.length) return $state.go('addclass'); 69 69 if (!_user.sections.length) return $state.go('addclass');
last_state = localStorage.getItem('last_state'); 70 70 last_state = localStorage.getItem('last_state');
if (last_state) { 71 71 if (last_state) {
// if there was a last state, get the parameters of the state 72 72 // if there was a last state, get the parameters of the state
last_state_params = JSON.parse(localStorage.getItem('last_state_params')); 73 73 last_state_params = JSON.parse(localStorage.getItem('last_state_params'));
if (last_state_params.sectionId && this.authorizedFor(last_state, last_state_params)) { 74 74 if (last_state_params.sectionId && this.authorizedFor(last_state, last_state_params)) {
// if we're authorized to access that state with those parameters, go there 75 75 // if we're authorized to access that state with those parameters, go there
return $state.go(last_state, JSON.parse(localStorage.getItem('last_state_params'))); 76 76 return $state.go(last_state, JSON.parse(localStorage.getItem('last_state_params')));
} 77 77 }
} 78 78 }
$state.go('feed', {sectionId: _user.sections[0].id}); 79 79 $state.go('feed', {sectionId: _user.sections[0].id});
}; 80 80 };
this.authorizedFor = function(state, stateParams) { 81 81 this.authorizedFor = function(state, stateParams) {
if (['feed', 'deck', 'cardlist'].indexOf(state.name) >= 0) { 82 82 if (['feed', 'deck', 'cardlist'].indexOf(state.name) >= 0) {
console.log('checking whether', stateParams, 'in', _user.sectionIdList); 83 83 console.log('checking whether', stateParams, 'in', _user.sectionIdList);
if (_user.sectionIdList.indexOf(parseInt(stateParams.sectionId)) < 0) { 84 84 if (_user.sectionIdList.indexOf(parseInt(stateParams.sectionId)) < 0) {
return false; 85 85 return false;
} 86 86 }
} 87 87 }
return true; 88 88 return true;
}; 89 89 };
this.showLockedMessage = function() { 90 90 this.showLockedMessage = function() {
Materialize.toast('You must verify your email address before continuing.' + 91 91 Materialize.toast('You must verify your email address before continuing.' +
'<a class="btn-flat cyan-text" onclick="rootscope.UserService.resendConfirmationEmail()">' + 92 92 '<a class="btn-flat cyan-text" onclick="rootscope.UserService.resendConfirmationEmail()">' +
'Resend Verification Email</a>', 4000); 93 93 'Resend Verification Email</a>', 4000);
}; 94 94 };
this.noAuthRequired = function(state) { 95 95 this.noAuthRequired = function(state) {
if (['verifyemail', 'login'].indexOf(state.name) >= 0) { 96 96 return ['verifyemail', 'login'].indexOf(state.name) >= 0;
return true; 97 97
} 98
return false; 99
}; 100 98 };
this.resendConfirmationEmail = function() { 101 99 this.resendConfirmationEmail = function() {
console.log('Requesting resend of confirmation email'); 102 100 console.log('Requesting resend of confirmation email');
$http.post('/api/resend_confirmation_email/').success(function() { 103 101 $http.post('/api/resend_confirmation_email/').success(function() {
Materialize.toast('Resent confirmation email! Check your spam folder too.', 4000); 104 102 Materialize.toast('Resent confirmation email! Check your spam folder too.', 4000);
}); 105 103 });
}; 106 104 };
}); 107 105 });
108 106
scripts/bootstrap.js View file @ f60d569
/*! 1 File was deleted
* Bootstrap v3.3.4 (http://getbootstrap.com) 2
* Copyright 2011-2015 Twitter, Inc. 3
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 4
*/ 5
6
if (typeof jQuery === 'undefined') { 7
throw new Error('Bootstrap\'s JavaScript requires jQuery') 8
} 9
10
+function ($) { 11
'use strict'; 12
var version = $.fn.jquery.split(' ')[0].split('.') 13
if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1)) { 14
throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher') 15
} 16
}(jQuery); 17
18
/* ======================================================================== 19
* Bootstrap: transition.js v3.3.4 20
* http://getbootstrap.com/javascript/#transitions 21
* ======================================================================== 22
* Copyright 2011-2015 Twitter, Inc. 23
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 24
* ======================================================================== */ 25
26
27
+function ($) { 28
'use strict'; 29
30
// CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) 31
// ============================================================ 32
33
function transitionEnd() { 34
var el = document.createElement('bootstrap') 35
36
var transEndEventNames = { 37
WebkitTransition : 'webkitTransitionEnd', 38
MozTransition : 'transitionend', 39
OTransition : 'oTransitionEnd otransitionend', 40
transition : 'transitionend' 41
} 42
43
for (var name in transEndEventNames) { 44
if (el.style[name] !== undefined) { 45
return { end: transEndEventNames[name] } 46
} 47
} 48
49
return false // explicit for ie8 ( ._.) 50
} 51
52
// http://blog.alexmaccaw.com/css-transitions 53
$.fn.emulateTransitionEnd = function (duration) { 54
var called = false 55
var $el = this 56
$(this).one('bsTransitionEnd', function () { called = true }) 57
var callback = function () { if (!called) $($el).trigger($.support.transition.end) } 58
setTimeout(callback, duration) 59
return this 60
} 61
62
$(function () { 63
$.support.transition = transitionEnd() 64
65
if (!$.support.transition) return 66
67
$.event.special.bsTransitionEnd = { 68
bindType: $.support.transition.end, 69
delegateType: $.support.transition.end, 70
handle: function (e) { 71
if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) 72
} 73
} 74
}) 75
76
}(jQuery); 77
78
/* ======================================================================== 79
* Bootstrap: alert.js v3.3.4 80
* http://getbootstrap.com/javascript/#alerts 81
* ======================================================================== 82
* Copyright 2011-2015 Twitter, Inc. 83
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 84
* ======================================================================== */ 85
86
87
+function ($) { 88
'use strict'; 89
90
// ALERT CLASS DEFINITION 91
// ====================== 92
93
var dismiss = '[data-dismiss="alert"]' 94
var Alert = function (el) { 95
$(el).on('click', dismiss, this.close) 96
} 97
98
Alert.VERSION = '3.3.4' 99
100
Alert.TRANSITION_DURATION = 150 101
102
Alert.prototype.close = function (e) { 103
var $this = $(this) 104
var selector = $this.attr('data-target') 105
106
if (!selector) { 107
selector = $this.attr('href') 108
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 109
} 110
111
var $parent = $(selector) 112
113
if (e) e.preventDefault() 114
115
if (!$parent.length) { 116
$parent = $this.closest('.alert') 117
} 118
119
$parent.trigger(e = $.Event('close.bs.alert')) 120
121
if (e.isDefaultPrevented()) return 122
123
$parent.removeClass('in') 124
125
function removeElement() { 126
// detach from parent, fire event then clean up data 127
$parent.detach().trigger('closed.bs.alert').remove() 128
} 129
130
$.support.transition && $parent.hasClass('fade') ? 131
$parent 132
.one('bsTransitionEnd', removeElement) 133
.emulateTransitionEnd(Alert.TRANSITION_DURATION) : 134
removeElement() 135
} 136
137
138
// ALERT PLUGIN DEFINITION 139
// ======================= 140
141
function Plugin(option) { 142
return this.each(function () { 143
var $this = $(this) 144
var data = $this.data('bs.alert') 145
146
if (!data) $this.data('bs.alert', (data = new Alert(this))) 147
if (typeof option == 'string') data[option].call($this) 148
}) 149
} 150
151
var old = $.fn.alert 152
153
$.fn.alert = Plugin 154
$.fn.alert.Constructor = Alert 155
156
157
// ALERT NO CONFLICT 158
// ================= 159
160
$.fn.alert.noConflict = function () { 161
$.fn.alert = old 162
return this 163
} 164
165
166
// ALERT DATA-API 167
// ============== 168
169
$(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) 170
171
}(jQuery); 172
173
/* ======================================================================== 174
* Bootstrap: button.js v3.3.4 175
* http://getbootstrap.com/javascript/#buttons 176
* ======================================================================== 177
* Copyright 2011-2015 Twitter, Inc. 178
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 179
* ======================================================================== */ 180
181
182
+function ($) { 183
'use strict'; 184
185
// BUTTON PUBLIC CLASS DEFINITION 186
// ============================== 187
188
var Button = function (element, options) { 189
this.$element = $(element) 190
this.options = $.extend({}, Button.DEFAULTS, options) 191
this.isLoading = false 192
} 193
194
Button.VERSION = '3.3.4' 195
196
Button.DEFAULTS = { 197
loadingText: 'loading...' 198
} 199
200
Button.prototype.setState = function (state) { 201
var d = 'disabled' 202
var $el = this.$element 203
var val = $el.is('input') ? 'val' : 'html' 204
var data = $el.data() 205
206
state = state + 'Text' 207
208
if (data.resetText == null) $el.data('resetText', $el[val]()) 209
210
// push to event loop to allow forms to submit 211
setTimeout($.proxy(function () { 212
$el[val](data[state] == null ? this.options[state] : data[state]) 213
214
if (state == 'loadingText') { 215
this.isLoading = true 216
$el.addClass(d).attr(d, d) 217
} else if (this.isLoading) { 218
this.isLoading = false 219
$el.removeClass(d).removeAttr(d) 220
} 221
}, this), 0) 222
} 223
224
Button.prototype.toggle = function () { 225
var changed = true 226
var $parent = this.$element.closest('[data-toggle="buttons"]') 227
228
if ($parent.length) { 229
var $input = this.$element.find('input') 230
if ($input.prop('type') == 'radio') { 231
if ($input.prop('checked') && this.$element.hasClass('active')) changed = false 232
else $parent.find('.active').removeClass('active') 233
} 234
if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change') 235
} else { 236
this.$element.attr('aria-pressed', !this.$element.hasClass('active')) 237
} 238
239
if (changed) this.$element.toggleClass('active') 240
} 241
242
243
// BUTTON PLUGIN DEFINITION 244
// ======================== 245
246
function Plugin(option) { 247
return this.each(function () { 248
var $this = $(this) 249
var data = $this.data('bs.button') 250
var options = typeof option == 'object' && option 251
252
if (!data) $this.data('bs.button', (data = new Button(this, options))) 253
254
if (option == 'toggle') data.toggle() 255
else if (option) data.setState(option) 256
}) 257
} 258
259
var old = $.fn.button 260
261
$.fn.button = Plugin 262
$.fn.button.Constructor = Button 263
264
265
// BUTTON NO CONFLICT 266
// ================== 267
268
$.fn.button.noConflict = function () { 269
$.fn.button = old 270
return this 271
} 272
273
274
// BUTTON DATA-API 275
// =============== 276
277
$(document) 278
.on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { 279
var $btn = $(e.target) 280
if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') 281
Plugin.call($btn, 'toggle') 282
e.preventDefault() 283
}) 284
.on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { 285
$(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type)) 286
}) 287
288
}(jQuery); 289
290
/* ======================================================================== 291
* Bootstrap: carousel.js v3.3.4 292
* http://getbootstrap.com/javascript/#carousel 293
* ======================================================================== 294
* Copyright 2011-2015 Twitter, Inc. 295
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 296
* ======================================================================== */ 297
298
299
+function ($) { 300
'use strict'; 301
302
// CAROUSEL CLASS DEFINITION 303
// ========================= 304
305
var Carousel = function (element, options) { 306
this.$element = $(element) 307
this.$indicators = this.$element.find('.carousel-indicators') 308
this.options = options 309
this.paused = null 310
this.sliding = null 311
this.interval = null 312
this.$active = null 313
this.$items = null 314
315
this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) 316
317
this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element 318
.on('mouseenter.bs.carousel', $.proxy(this.pause, this)) 319
.on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) 320
} 321
322
Carousel.VERSION = '3.3.4' 323
324
Carousel.TRANSITION_DURATION = 600 325
326
Carousel.DEFAULTS = { 327
interval: 5000, 328
pause: 'hover', 329
wrap: true, 330
keyboard: true 331
} 332
333
Carousel.prototype.keydown = function (e) { 334
if (/input|textarea/i.test(e.target.tagName)) return 335
switch (e.which) { 336
case 37: this.prev(); break 337
case 39: this.next(); break 338
default: return 339
} 340
341
e.preventDefault() 342
} 343
344
Carousel.prototype.cycle = function (e) { 345
e || (this.paused = false) 346
347
this.interval && clearInterval(this.interval) 348
349
this.options.interval 350
&& !this.paused 351
&& (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) 352
353
return this 354
} 355
356
Carousel.prototype.getItemIndex = function (item) { 357
this.$items = item.parent().children('.item') 358
return this.$items.index(item || this.$active) 359
} 360
361
Carousel.prototype.getItemForDirection = function (direction, active) { 362
var activeIndex = this.getItemIndex(active) 363
var willWrap = (direction == 'prev' && activeIndex === 0) 364
|| (direction == 'next' && activeIndex == (this.$items.length - 1)) 365
if (willWrap && !this.options.wrap) return active 366
var delta = direction == 'prev' ? -1 : 1 367
var itemIndex = (activeIndex + delta) % this.$items.length 368
return this.$items.eq(itemIndex) 369
} 370
371
Carousel.prototype.to = function (pos) { 372
var that = this 373
var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) 374
375
if (pos > (this.$items.length - 1) || pos < 0) return 376
377
if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" 378
if (activeIndex == pos) return this.pause().cycle() 379
380
return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) 381
} 382
383
Carousel.prototype.pause = function (e) { 384
e || (this.paused = true) 385
386
if (this.$element.find('.next, .prev').length && $.support.transition) { 387
this.$element.trigger($.support.transition.end) 388
this.cycle(true) 389
} 390
391
this.interval = clearInterval(this.interval) 392
393
return this 394
} 395
396
Carousel.prototype.next = function () { 397
if (this.sliding) return 398
return this.slide('next') 399
} 400
401
Carousel.prototype.prev = function () { 402
if (this.sliding) return 403
return this.slide('prev') 404
} 405
406
Carousel.prototype.slide = function (type, next) { 407
var $active = this.$element.find('.item.active') 408
var $next = next || this.getItemForDirection(type, $active) 409
var isCycling = this.interval 410
var direction = type == 'next' ? 'left' : 'right' 411
var that = this 412
413
if ($next.hasClass('active')) return (this.sliding = false) 414
415
var relatedTarget = $next[0] 416
var slideEvent = $.Event('slide.bs.carousel', { 417
relatedTarget: relatedTarget, 418
direction: direction 419
}) 420
this.$element.trigger(slideEvent) 421
if (slideEvent.isDefaultPrevented()) return 422
423
this.sliding = true 424
425
isCycling && this.pause() 426
427
if (this.$indicators.length) { 428
this.$indicators.find('.active').removeClass('active') 429
var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) 430
$nextIndicator && $nextIndicator.addClass('active') 431
} 432
433
var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" 434
if ($.support.transition && this.$element.hasClass('slide')) { 435
$next.addClass(type) 436
$next[0].offsetWidth // force reflow 437
$active.addClass(direction) 438
$next.addClass(direction) 439
$active 440
.one('bsTransitionEnd', function () { 441
$next.removeClass([type, direction].join(' ')).addClass('active') 442
$active.removeClass(['active', direction].join(' ')) 443
that.sliding = false 444
setTimeout(function () { 445
that.$element.trigger(slidEvent) 446
}, 0) 447
}) 448
.emulateTransitionEnd(Carousel.TRANSITION_DURATION) 449
} else { 450
$active.removeClass('active') 451
$next.addClass('active') 452
this.sliding = false 453
this.$element.trigger(slidEvent) 454
} 455
456
isCycling && this.cycle() 457
458
return this 459
} 460
461
462
// CAROUSEL PLUGIN DEFINITION 463
// ========================== 464
465
function Plugin(option) { 466
return this.each(function () { 467
var $this = $(this) 468
var data = $this.data('bs.carousel') 469
var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) 470
var action = typeof option == 'string' ? option : options.slide 471
472
if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) 473
if (typeof option == 'number') data.to(option) 474
else if (action) data[action]() 475
else if (options.interval) data.pause().cycle() 476
}) 477
} 478
479
var old = $.fn.carousel 480
481
$.fn.carousel = Plugin 482
$.fn.carousel.Constructor = Carousel 483
484
485
// CAROUSEL NO CONFLICT 486
// ==================== 487
488
$.fn.carousel.noConflict = function () { 489
$.fn.carousel = old 490
return this 491
} 492
493
494
// CAROUSEL DATA-API 495
// ================= 496
497
var clickHandler = function (e) { 498
var href 499
var $this = $(this) 500
var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 501
if (!$target.hasClass('carousel')) return 502
var options = $.extend({}, $target.data(), $this.data()) 503
var slideIndex = $this.attr('data-slide-to') 504
if (slideIndex) options.interval = false 505
506
Plugin.call($target, options) 507
508
if (slideIndex) { 509
$target.data('bs.carousel').to(slideIndex) 510
} 511
512
e.preventDefault() 513
} 514
515
$(document) 516
.on('click.bs.carousel.data-api', '[data-slide]', clickHandler) 517
.on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler) 518
519
$(window).on('load', function () { 520
$('[data-ride="carousel"]').each(function () { 521
var $carousel = $(this) 522
Plugin.call($carousel, $carousel.data()) 523
}) 524
}) 525
526
}(jQuery); 527
528
/* ======================================================================== 529
* Bootstrap: collapse.js v3.3.4 530
* http://getbootstrap.com/javascript/#collapse 531
* ======================================================================== 532
* Copyright 2011-2015 Twitter, Inc. 533
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 534
* ======================================================================== */ 535
536
537
+function ($) { 538
'use strict'; 539
540
// COLLAPSE PUBLIC CLASS DEFINITION 541
// ================================ 542
543
var Collapse = function (element, options) { 544
this.$element = $(element) 545
this.options = $.extend({}, Collapse.DEFAULTS, options) 546
this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' + 547
'[data-toggle="collapse"][data-target="#' + element.id + '"]') 548
this.transitioning = null 549
550
if (this.options.parent) { 551
this.$parent = this.getParent() 552
} else { 553
this.addAriaAndCollapsedClass(this.$element, this.$trigger) 554
} 555
556
if (this.options.toggle) this.toggle() 557
} 558
559
Collapse.VERSION = '3.3.4' 560
561
Collapse.TRANSITION_DURATION = 350 562
563
Collapse.DEFAULTS = { 564
toggle: true 565
} 566
567
Collapse.prototype.dimension = function () { 568
var hasWidth = this.$element.hasClass('width') 569
return hasWidth ? 'width' : 'height' 570
} 571
572
Collapse.prototype.show = function () { 573
if (this.transitioning || this.$element.hasClass('in')) return 574
575
var activesData 576
var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing') 577
578
if (actives && actives.length) { 579
activesData = actives.data('bs.collapse') 580
if (activesData && activesData.transitioning) return 581
} 582
583
var startEvent = $.Event('show.bs.collapse') 584
this.$element.trigger(startEvent) 585
if (startEvent.isDefaultPrevented()) return 586
587
if (actives && actives.length) { 588
Plugin.call(actives, 'hide') 589
activesData || actives.data('bs.collapse', null) 590
} 591
592
var dimension = this.dimension() 593
594
this.$element 595
.removeClass('collapse') 596
.addClass('collapsing')[dimension](0) 597
.attr('aria-expanded', true) 598
599
this.$trigger 600
.removeClass('collapsed') 601
.attr('aria-expanded', true) 602
603
this.transitioning = 1 604
605
var complete = function () { 606
this.$element 607
.removeClass('collapsing') 608
.addClass('collapse in')[dimension]('') 609
this.transitioning = 0 610
this.$element 611
.trigger('shown.bs.collapse') 612
} 613
614
if (!$.support.transition) return complete.call(this) 615
616
var scrollSize = $.camelCase(['scroll', dimension].join('-')) 617
618
this.$element 619
.one('bsTransitionEnd', $.proxy(complete, this)) 620
.emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) 621
} 622
623
Collapse.prototype.hide = function () { 624
if (this.transitioning || !this.$element.hasClass('in')) return 625
626
var startEvent = $.Event('hide.bs.collapse') 627
this.$element.trigger(startEvent) 628
if (startEvent.isDefaultPrevented()) return 629
630
var dimension = this.dimension() 631
632
this.$element[dimension](this.$element[dimension]())[0].offsetHeight 633
634
this.$element 635
.addClass('collapsing') 636
.removeClass('collapse in') 637
.attr('aria-expanded', false) 638
639
this.$trigger 640
.addClass('collapsed') 641
.attr('aria-expanded', false) 642
643
this.transitioning = 1 644
645
var complete = function () { 646
this.transitioning = 0 647
this.$element 648
.removeClass('collapsing') 649
.addClass('collapse') 650
.trigger('hidden.bs.collapse') 651
} 652
653
if (!$.support.transition) return complete.call(this) 654
655
this.$element 656
[dimension](0) 657
.one('bsTransitionEnd', $.proxy(complete, this)) 658
.emulateTransitionEnd(Collapse.TRANSITION_DURATION) 659
} 660
661
Collapse.prototype.toggle = function () { 662
this[this.$element.hasClass('in') ? 'hide' : 'show']() 663
} 664
665
Collapse.prototype.getParent = function () { 666
return $(this.options.parent) 667
.find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]') 668
.each($.proxy(function (i, element) { 669
var $element = $(element) 670
this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element) 671
}, this)) 672
.end() 673
} 674
675
Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) { 676
var isOpen = $element.hasClass('in') 677
678
$element.attr('aria-expanded', isOpen) 679
$trigger 680
.toggleClass('collapsed', !isOpen) 681
.attr('aria-expanded', isOpen) 682
} 683
684
function getTargetFromTrigger($trigger) { 685
var href 686
var target = $trigger.attr('data-target') 687
|| (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 688
689
return $(target) 690
} 691
692
693
// COLLAPSE PLUGIN DEFINITION 694
// ========================== 695
696
function Plugin(option) { 697
return this.each(function () { 698
var $this = $(this) 699
var data = $this.data('bs.collapse') 700
var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) 701
702
if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false 703
if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) 704
if (typeof option == 'string') data[option]() 705
}) 706
} 707
708
var old = $.fn.collapse 709
710
$.fn.collapse = Plugin 711
$.fn.collapse.Constructor = Collapse 712
713
714
// COLLAPSE NO CONFLICT 715
// ==================== 716
717
$.fn.collapse.noConflict = function () { 718
$.fn.collapse = old 719
return this 720
} 721
722
723
// COLLAPSE DATA-API 724
// ================= 725
726
$(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { 727
var $this = $(this) 728
729
if (!$this.attr('data-target')) e.preventDefault() 730
731
var $target = getTargetFromTrigger($this) 732
var data = $target.data('bs.collapse') 733
var option = data ? 'toggle' : $this.data() 734
735
Plugin.call($target, option) 736
}) 737
738
}(jQuery); 739
740
/* ======================================================================== 741
* Bootstrap: dropdown.js v3.3.4 742
* http://getbootstrap.com/javascript/#dropdowns 743
* ======================================================================== 744
* Copyright 2011-2015 Twitter, Inc. 745
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 746
* ======================================================================== */ 747
748
749
+function ($) { 750
'use strict'; 751
752
// DROPDOWN CLASS DEFINITION 753
// ========================= 754
755
var backdrop = '.dropdown-backdrop' 756
var toggle = '[data-toggle="dropdown"]' 757
var Dropdown = function (element) { 758
$(element).on('click.bs.dropdown', this.toggle) 759
} 760
761
Dropdown.VERSION = '3.3.4' 762
763
Dropdown.prototype.toggle = function (e) { 764
var $this = $(this) 765
766
if ($this.is('.disabled, :disabled')) return 767
768
var $parent = getParent($this) 769
var isActive = $parent.hasClass('open') 770
771
clearMenus() 772
773
if (!isActive) { 774
if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { 775
// if mobile we use a backdrop because click events don't delegate 776
$('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus) 777
} 778
779
var relatedTarget = { relatedTarget: this } 780
$parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget)) 781
782
if (e.isDefaultPrevented()) return 783
784
$this 785
.trigger('focus') 786
.attr('aria-expanded', 'true') 787
788
$parent 789
.toggleClass('open') 790
.trigger('shown.bs.dropdown', relatedTarget) 791
} 792
793
return false 794
} 795
796
Dropdown.prototype.keydown = function (e) { 797
if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return 798
799
var $this = $(this) 800
801
e.preventDefault() 802
e.stopPropagation() 803
804
if ($this.is('.disabled, :disabled')) return 805
806
var $parent = getParent($this) 807
var isActive = $parent.hasClass('open') 808
809
if ((!isActive && e.which != 27) || (isActive && e.which == 27)) { 810
if (e.which == 27) $parent.find(toggle).trigger('focus') 811
return $this.trigger('click') 812
} 813
814
var desc = ' li:not(.disabled):visible a' 815
var $items = $parent.find('[role="menu"]' + desc + ', [role="listbox"]' + desc) 816
817
if (!$items.length) return 818
819
var index = $items.index(e.target) 820
821
if (e.which == 38 && index > 0) index-- // up 822
if (e.which == 40 && index < $items.length - 1) index++ // down 823
if (!~index) index = 0 824
825
$items.eq(index).trigger('focus') 826
} 827
828
function clearMenus(e) { 829
if (e && e.which === 3) return 830
$(backdrop).remove() 831
$(toggle).each(function () { 832
var $this = $(this) 833
var $parent = getParent($this) 834
var relatedTarget = { relatedTarget: this } 835
836
if (!$parent.hasClass('open')) return 837
838
$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget)) 839
840
if (e.isDefaultPrevented()) return 841
842
$this.attr('aria-expanded', 'false') 843
$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget) 844
}) 845
} 846
847
function getParent($this) { 848
var selector = $this.attr('data-target') 849
850
if (!selector) { 851
selector = $this.attr('href') 852
selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 853
} 854
855
var $parent = selector && $(selector) 856
857
return $parent && $parent.length ? $parent : $this.parent() 858
} 859
860
861
// DROPDOWN PLUGIN DEFINITION 862
// ========================== 863
864
function Plugin(option) { 865
return this.each(function () { 866
var $this = $(this) 867
var data = $this.data('bs.dropdown') 868
869
if (!data) $this.data('bs.dropdown', (data = new Dropdown(this))) 870
if (typeof option == 'string') data[option].call($this) 871
}) 872
} 873
874
var old = $.fn.dropdown 875
876
$.fn.dropdown = Plugin 877
$.fn.dropdown.Constructor = Dropdown 878
879
880
// DROPDOWN NO CONFLICT 881
// ==================== 882
883
$.fn.dropdown.noConflict = function () { 884
$.fn.dropdown = old 885
return this 886
} 887
888
889
// APPLY TO STANDARD DROPDOWN ELEMENTS 890
// =================================== 891
892
$(document) 893
.on('click.bs.dropdown.data-api', clearMenus) 894
.on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) 895
.on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle) 896
.on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown) 897
.on('keydown.bs.dropdown.data-api', '[role="menu"]', Dropdown.prototype.keydown) 898
.on('keydown.bs.dropdown.data-api', '[role="listbox"]', Dropdown.prototype.keydown) 899
900
}(jQuery); 901
902
/* ======================================================================== 903
* Bootstrap: modal.js v3.3.4 904
* http://getbootstrap.com/javascript/#modals 905
* ======================================================================== 906
* Copyright 2011-2015 Twitter, Inc. 907
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 908
* ======================================================================== */ 909
910
911
+function ($) { 912
'use strict'; 913
914
// MODAL CLASS DEFINITION 915
// ====================== 916
917
var Modal = function (element, options) { 918
this.options = options 919
this.$body = $(document.body) 920
this.$element = $(element) 921
this.$dialog = this.$element.find('.modal-dialog') 922
this.$backdrop = null 923
this.isShown = null 924
this.originalBodyPad = null 925
this.scrollbarWidth = 0 926
this.ignoreBackdropClick = false 927
928
if (this.options.remote) { 929
this.$element 930
.find('.modal-content') 931
.load(this.options.remote, $.proxy(function () { 932
this.$element.trigger('loaded.bs.modal') 933
}, this)) 934
} 935
} 936
937
Modal.VERSION = '3.3.4' 938
939
Modal.TRANSITION_DURATION = 300 940
Modal.BACKDROP_TRANSITION_DURATION = 150 941
942
Modal.DEFAULTS = { 943
backdrop: true, 944
keyboard: true, 945
show: true 946
} 947
948
Modal.prototype.toggle = function (_relatedTarget) { 949
return this.isShown ? this.hide() : this.show(_relatedTarget) 950
} 951
952
Modal.prototype.show = function (_relatedTarget) { 953
var that = this 954
var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget }) 955
956
this.$element.trigger(e) 957
958
if (this.isShown || e.isDefaultPrevented()) return 959
960
this.isShown = true 961
962
this.checkScrollbar() 963
this.setScrollbar() 964
this.$body.addClass('modal-open') 965
966
this.escape() 967
this.resize() 968
969
this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this)) 970
971
this.$dialog.on('mousedown.dismiss.bs.modal', function () { 972
that.$element.one('mouseup.dismiss.bs.modal', function (e) { 973
if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true 974
}) 975
}) 976
977
this.backdrop(function () { 978
var transition = $.support.transition && that.$element.hasClass('fade') 979
980
if (!that.$element.parent().length) { 981
that.$element.appendTo(that.$body) // don't move modals dom position 982
} 983
984
that.$element 985
.show() 986
.scrollTop(0) 987
988
that.adjustDialog() 989
990
if (transition) { 991
that.$element[0].offsetWidth // force reflow 992
} 993
994
that.$element 995
.addClass('in') 996
.attr('aria-hidden', false) 997
998
that.enforceFocus() 999
1000
var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget }) 1001
1002
transition ? 1003
that.$dialog // wait for modal to slide in 1004
.one('bsTransitionEnd', function () { 1005
that.$element.trigger('focus').trigger(e) 1006
}) 1007
.emulateTransitionEnd(Modal.TRANSITION_DURATION) : 1008
that.$element.trigger('focus').trigger(e) 1009
}) 1010
} 1011
1012
Modal.prototype.hide = function (e) { 1013
if (e) e.preventDefault() 1014
1015
e = $.Event('hide.bs.modal') 1016
1017
this.$element.trigger(e) 1018
1019
if (!this.isShown || e.isDefaultPrevented()) return 1020
1021
this.isShown = false 1022
1023
this.escape() 1024
this.resize() 1025
1026
$(document).off('focusin.bs.modal') 1027
1028
this.$element 1029
.removeClass('in') 1030
.attr('aria-hidden', true) 1031
.off('click.dismiss.bs.modal') 1032
.off('mouseup.dismiss.bs.modal') 1033
1034
this.$dialog.off('mousedown.dismiss.bs.modal') 1035
1036
$.support.transition && this.$element.hasClass('fade') ? 1037
this.$element 1038
.one('bsTransitionEnd', $.proxy(this.hideModal, this)) 1039
.emulateTransitionEnd(Modal.TRANSITION_DURATION) : 1040
this.hideModal() 1041
} 1042
1043
Modal.prototype.enforceFocus = function () { 1044
$(document) 1045
.off('focusin.bs.modal') // guard against infinite focus loop 1046
.on('focusin.bs.modal', $.proxy(function (e) { 1047
if (this.$element[0] !== e.target && !this.$element.has(e.target).length) { 1048
this.$element.trigger('focus') 1049
} 1050
}, this)) 1051
} 1052
1053
Modal.prototype.escape = function () { 1054
if (this.isShown && this.options.keyboard) { 1055
this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) { 1056
e.which == 27 && this.hide() 1057
}, this)) 1058
} else if (!this.isShown) { 1059
this.$element.off('keydown.dismiss.bs.modal') 1060
} 1061
} 1062
1063
Modal.prototype.resize = function () { 1064
if (this.isShown) { 1065
$(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this)) 1066
} else { 1067
$(window).off('resize.bs.modal') 1068
} 1069
} 1070
1071
Modal.prototype.hideModal = function () { 1072
var that = this 1073
this.$element.hide() 1074
this.backdrop(function () { 1075
that.$body.removeClass('modal-open') 1076
that.resetAdjustments() 1077
that.resetScrollbar() 1078
that.$element.trigger('hidden.bs.modal') 1079
}) 1080
} 1081
1082
Modal.prototype.removeBackdrop = function () { 1083
this.$backdrop && this.$backdrop.remove() 1084
this.$backdrop = null 1085
} 1086
1087
Modal.prototype.backdrop = function (callback) { 1088
var that = this 1089
var animate = this.$element.hasClass('fade') ? 'fade' : '' 1090
1091
if (this.isShown && this.options.backdrop) { 1092
var doAnimate = $.support.transition && animate 1093
1094
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />') 1095
.appendTo(this.$body) 1096
1097
this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) { 1098
if (this.ignoreBackdropClick) { 1099
this.ignoreBackdropClick = false 1100
return 1101
} 1102
if (e.target !== e.currentTarget) return 1103
this.options.backdrop == 'static' 1104
? this.$element[0].focus() 1105
: this.hide() 1106
}, this)) 1107
1108
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow 1109
1110
this.$backdrop.addClass('in') 1111
1112
if (!callback) return 1113
1114
doAnimate ? 1115
this.$backdrop 1116
.one('bsTransitionEnd', callback) 1117
.emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : 1118
callback() 1119
1120
} else if (!this.isShown && this.$backdrop) { 1121
this.$backdrop.removeClass('in') 1122
1123
var callbackRemove = function () { 1124
that.removeBackdrop() 1125
callback && callback() 1126
} 1127
$.support.transition && this.$element.hasClass('fade') ? 1128
this.$backdrop 1129
.one('bsTransitionEnd', callbackRemove) 1130
.emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : 1131
callbackRemove() 1132
1133
} else if (callback) { 1134
callback() 1135
} 1136
} 1137
1138
// these following methods are used to handle overflowing modals 1139
1140
Modal.prototype.handleUpdate = function () { 1141
this.adjustDialog() 1142
} 1143
1144
Modal.prototype.adjustDialog = function () { 1145
var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight 1146
1147
this.$element.css({ 1148
paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '', 1149
paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : '' 1150
}) 1151
} 1152
1153
Modal.prototype.resetAdjustments = function () { 1154
this.$element.css({ 1155
paddingLeft: '', 1156
paddingRight: '' 1157
}) 1158
} 1159
1160
Modal.prototype.checkScrollbar = function () { 1161
var fullWindowWidth = window.innerWidth 1162
if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8 1163
var documentElementRect = document.documentElement.getBoundingClientRect() 1164
fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left) 1165
} 1166
this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth 1167
this.scrollbarWidth = this.measureScrollbar() 1168
} 1169
1170
Modal.prototype.setScrollbar = function () { 1171
var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10) 1172
this.originalBodyPad = document.body.style.paddingRight || '' 1173
if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth) 1174
} 1175
1176
Modal.prototype.resetScrollbar = function () { 1177
this.$body.css('padding-right', this.originalBodyPad) 1178
} 1179
1180
Modal.prototype.measureScrollbar = function () { // thx walsh 1181
var scrollDiv = document.createElement('div') 1182
scrollDiv.className = 'modal-scrollbar-measure' 1183
this.$body.append(scrollDiv) 1184
var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth 1185
this.$body[0].removeChild(scrollDiv) 1186
return scrollbarWidth 1187
} 1188
1189
1190
// MODAL PLUGIN DEFINITION 1191
// ======================= 1192
1193
function Plugin(option, _relatedTarget) { 1194
return this.each(function () { 1195
var $this = $(this) 1196
var data = $this.data('bs.modal') 1197
var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option) 1198
1199
if (!data) $this.data('bs.modal', (data = new Modal(this, options))) 1200
if (typeof option == 'string') data[option](_relatedTarget) 1201
else if (options.show) data.show(_relatedTarget) 1202
}) 1203
} 1204
1205
var old = $.fn.modal 1206
1207
$.fn.modal = Plugin 1208
$.fn.modal.Constructor = Modal 1209
1210
1211
// MODAL NO CONFLICT 1212
// ================= 1213
1214
$.fn.modal.noConflict = function () { 1215
$.fn.modal = old 1216
return this 1217
} 1218
1219
1220
// MODAL DATA-API 1221
// ============== 1222
1223
$(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) { 1224
var $this = $(this) 1225
var href = $this.attr('href') 1226
var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7 1227
var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data()) 1228
1229
if ($this.is('a')) e.preventDefault() 1230
1231
$target.one('show.bs.modal', function (showEvent) { 1232
if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown 1233
$target.one('hidden.bs.modal', function () { 1234
$this.is(':visible') && $this.trigger('focus') 1235
}) 1236
}) 1237
Plugin.call($target, option, this) 1238
}) 1239
1240
}(jQuery); 1241
1242
/* ======================================================================== 1243
* Bootstrap: tooltip.js v3.3.4 1244
* http://getbootstrap.com/javascript/#tooltip 1245
* Inspired by the original jQuery.tipsy by Jason Frame 1246
* ======================================================================== 1247
* Copyright 2011-2015 Twitter, Inc. 1248
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 1249
* ======================================================================== */ 1250
1251
1252
+function ($) { 1253
'use strict'; 1254
1255
// TOOLTIP PUBLIC CLASS DEFINITION 1256
// =============================== 1257
1258
var Tooltip = function (element, options) { 1259
this.type = null 1260
this.options = null 1261
this.enabled = null 1262
this.timeout = null 1263
this.hoverState = null 1264
this.$element = null 1265
1266
this.init('tooltip', element, options) 1267
} 1268
1269
Tooltip.VERSION = '3.3.4' 1270
1271
Tooltip.TRANSITION_DURATION = 150 1272
1273
Tooltip.DEFAULTS = { 1274
animation: true, 1275
placement: 'top', 1276
selector: false, 1277
template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>', 1278
trigger: 'hover focus', 1279
title: '', 1280
delay: 0, 1281
html: false, 1282
container: false, 1283
viewport: { 1284
selector: 'body', 1285
padding: 0 1286
} 1287
} 1288
1289
Tooltip.prototype.init = function (type, element, options) { 1290
this.enabled = true 1291
this.type = type 1292
this.$element = $(element) 1293
this.options = this.getOptions(options) 1294
this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport) 1295
1296
if (this.$element[0] instanceof document.constructor && !this.options.selector) { 1297
throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!') 1298
} 1299
1300
var triggers = this.options.trigger.split(' ') 1301
1302
for (var i = triggers.length; i--;) { 1303
var trigger = triggers[i] 1304
1305
if (trigger == 'click') { 1306
this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) 1307
} else if (trigger != 'manual') { 1308
var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin' 1309
var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout' 1310
1311
this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) 1312
this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) 1313
} 1314
} 1315
1316
this.options.selector ? 1317
(this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : 1318
this.fixTitle() 1319
} 1320
1321
Tooltip.prototype.getDefaults = function () { 1322
return Tooltip.DEFAULTS 1323
} 1324
1325
Tooltip.prototype.getOptions = function (options) { 1326
options = $.extend({}, this.getDefaults(), this.$element.data(), options) 1327
1328
if (options.delay && typeof options.delay == 'number') { 1329
options.delay = { 1330
show: options.delay, 1331
hide: options.delay 1332
} 1333
} 1334
1335
return options 1336
} 1337
1338
Tooltip.prototype.getDelegateOptions = function () { 1339
var options = {} 1340
var defaults = this.getDefaults() 1341
1342
this._options && $.each(this._options, function (key, value) { 1343
if (defaults[key] != value) options[key] = value 1344
}) 1345
1346
return options 1347
} 1348
1349
Tooltip.prototype.enter = function (obj) { 1350
var self = obj instanceof this.constructor ? 1351
obj : $(obj.currentTarget).data('bs.' + this.type) 1352
1353
if (self && self.$tip && self.$tip.is(':visible')) { 1354
self.hoverState = 'in' 1355
return 1356
} 1357
1358
if (!self) { 1359
self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) 1360
$(obj.currentTarget).data('bs.' + this.type, self) 1361
} 1362
1363
clearTimeout(self.timeout) 1364
1365
self.hoverState = 'in' 1366
1367
if (!self.options.delay || !self.options.delay.show) return self.show() 1368
1369
self.timeout = setTimeout(function () { 1370
if (self.hoverState == 'in') self.show() 1371
}, self.options.delay.show) 1372
} 1373
1374
Tooltip.prototype.leave = function (obj) { 1375
var self = obj instanceof this.constructor ? 1376
obj : $(obj.currentTarget).data('bs.' + this.type) 1377
1378
if (!self) { 1379
self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) 1380
$(obj.currentTarget).data('bs.' + this.type, self) 1381
} 1382
1383
clearTimeout(self.timeout) 1384
1385
self.hoverState = 'out' 1386
1387
if (!self.options.delay || !self.options.delay.hide) return self.hide() 1388
1389
self.timeout = setTimeout(function () { 1390
if (self.hoverState == 'out') self.hide() 1391
}, self.options.delay.hide) 1392
} 1393
1394
Tooltip.prototype.show = function () { 1395
var e = $.Event('show.bs.' + this.type) 1396
1397
if (this.hasContent() && this.enabled) { 1398
this.$element.trigger(e) 1399
1400
var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0]) 1401
if (e.isDefaultPrevented() || !inDom) return 1402
var that = this 1403
1404
var $tip = this.tip() 1405
1406
var tipId = this.getUID(this.type) 1407
1408
this.setContent() 1409
$tip.attr('id', tipId) 1410
this.$element.attr('aria-describedby', tipId) 1411
1412
if (this.options.animation) $tip.addClass('fade') 1413
1414
var placement = typeof this.options.placement == 'function' ? 1415
this.options.placement.call(this, $tip[0], this.$element[0]) : 1416
this.options.placement 1417
1418
var autoToken = /\s?auto?\s?/i 1419
var autoPlace = autoToken.test(placement) 1420
if (autoPlace) placement = placement.replace(autoToken, '') || 'top' 1421
1422
$tip 1423
.detach() 1424
.css({ top: 0, left: 0, display: 'block' }) 1425
.addClass(placement) 1426
.data('bs.' + this.type, this) 1427
1428
this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) 1429
1430
var pos = this.getPosition() 1431
var actualWidth = $tip[0].offsetWidth 1432
var actualHeight = $tip[0].offsetHeight 1433
1434
if (autoPlace) { 1435
var orgPlacement = placement 1436
var $container = this.options.container ? $(this.options.container) : this.$element.parent() 1437
var containerDim = this.getPosition($container) 1438
1439
placement = placement == 'bottom' && pos.bottom + actualHeight > containerDim.bottom ? 'top' : 1440
placement == 'top' && pos.top - actualHeight < containerDim.top ? 'bottom' : 1441
placement == 'right' && pos.right + actualWidth > containerDim.width ? 'left' : 1442
placement == 'left' && pos.left - actualWidth < containerDim.left ? 'right' : 1443
placement 1444
1445
$tip 1446
.removeClass(orgPlacement) 1447
.addClass(placement) 1448
} 1449
1450
var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight) 1451
1452
this.applyPlacement(calculatedOffset, placement) 1453
1454
var complete = function () { 1455
var prevHoverState = that.hoverState 1456
that.$element.trigger('shown.bs.' + that.type) 1457
that.hoverState = null 1458
1459
if (prevHoverState == 'out') that.leave(that) 1460
} 1461
1462
$.support.transition && this.$tip.hasClass('fade') ? 1463
$tip 1464
.one('bsTransitionEnd', complete) 1465
.emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : 1466
complete() 1467
} 1468
} 1469
1470
Tooltip.prototype.applyPlacement = function (offset, placement) { 1471
var $tip = this.tip() 1472
var width = $tip[0].offsetWidth 1473
var height = $tip[0].offsetHeight 1474
1475
// manually read margins because getBoundingClientRect includes difference 1476
var marginTop = parseInt($tip.css('margin-top'), 10) 1477
var marginLeft = parseInt($tip.css('margin-left'), 10) 1478
1479
// we must check for NaN for ie 8/9 1480
if (isNaN(marginTop)) marginTop = 0 1481
if (isNaN(marginLeft)) marginLeft = 0 1482
1483
offset.top = offset.top + marginTop 1484
offset.left = offset.left + marginLeft 1485
1486
// $.fn.offset doesn't round pixel values 1487
// so we use setOffset directly with our own function B-0 1488
$.offset.setOffset($tip[0], $.extend({ 1489
using: function (props) { 1490
$tip.css({ 1491
top: Math.round(props.top), 1492
left: Math.round(props.left) 1493
}) 1494
} 1495
}, offset), 0) 1496
1497
$tip.addClass('in') 1498
1499
// check to see if placing tip in new offset caused the tip to resize itself 1500
var actualWidth = $tip[0].offsetWidth 1501
var actualHeight = $tip[0].offsetHeight 1502
1503
if (placement == 'top' && actualHeight != height) { 1504
offset.top = offset.top + height - actualHeight 1505
} 1506
1507
var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight) 1508
1509
if (delta.left) offset.left += delta.left 1510
else offset.top += delta.top 1511
1512
var isVertical = /top|bottom/.test(placement) 1513
var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight 1514
var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight' 1515
1516
$tip.offset(offset) 1517
this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical) 1518
} 1519
1520
Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) { 1521
this.arrow() 1522
.css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%') 1523
.css(isVertical ? 'top' : 'left', '') 1524
} 1525
1526
Tooltip.prototype.setContent = function () { 1527
var $tip = this.tip() 1528
var title = this.getTitle() 1529
1530
$tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) 1531
$tip.removeClass('fade in top bottom left right') 1532
} 1533
1534
Tooltip.prototype.hide = function (callback) { 1535
var that = this 1536
var $tip = $(this.$tip) 1537
var e = $.Event('hide.bs.' + this.type) 1538
1539
function complete() { 1540
if (that.hoverState != 'in') $tip.detach() 1541
that.$element 1542
.removeAttr('aria-describedby') 1543
.trigger('hidden.bs.' + that.type) 1544
callback && callback() 1545
} 1546
1547
this.$element.trigger(e) 1548
1549
if (e.isDefaultPrevented()) return 1550
1551
$tip.removeClass('in') 1552
1553
$.support.transition && $tip.hasClass('fade') ? 1554
$tip 1555
.one('bsTransitionEnd', complete) 1556
.emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : 1557
complete() 1558
1559
this.hoverState = null 1560
1561
return this 1562
} 1563
1564
Tooltip.prototype.fixTitle = function () { 1565
var $e = this.$element 1566
if ($e.attr('title') || typeof ($e.attr('data-original-title')) != 'string') { 1567
$e.attr('data-original-title', $e.attr('title') || '').attr('title', '') 1568
} 1569
} 1570
1571
Tooltip.prototype.hasContent = function () { 1572
return this.getTitle() 1573
} 1574
1575
Tooltip.prototype.getPosition = function ($element) { 1576
$element = $element || this.$element 1577
1578
var el = $element[0] 1579
var isBody = el.tagName == 'BODY' 1580
1581
var elRect = el.getBoundingClientRect() 1582
if (elRect.width == null) { 1583
// width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093 1584
elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top }) 1585
} 1586
var elOffset = isBody ? { top: 0, left: 0 } : $element.offset() 1587
var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() } 1588
var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null 1589
1590
return $.extend({}, elRect, scroll, outerDims, elOffset) 1591
} 1592
1593
Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) { 1594
return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : 1595
placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } : 1596
placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } : 1597
/* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } 1598
1599
} 1600
1601
Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) { 1602
var delta = { top: 0, left: 0 } 1603
if (!this.$viewport) return delta 1604
1605
var viewportPadding = this.options.viewport && this.options.viewport.padding || 0 1606
var viewportDimensions = this.getPosition(this.$viewport) 1607
1608
if (/right|left/.test(placement)) { 1609
var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll 1610
var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight 1611
if (topEdgeOffset < viewportDimensions.top) { // top overflow 1612
delta.top = viewportDimensions.top - topEdgeOffset 1613
} else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow 1614
delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset 1615
} 1616
} else { 1617
var leftEdgeOffset = pos.left - viewportPadding 1618
var rightEdgeOffset = pos.left + viewportPadding + actualWidth 1619
if (leftEdgeOffset < viewportDimensions.left) { // left overflow 1620
delta.left = viewportDimensions.left - leftEdgeOffset 1621
} else if (rightEdgeOffset > viewportDimensions.width) { // right overflow 1622
delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset 1623
} 1624
} 1625
1626
return delta 1627
} 1628
1629
Tooltip.prototype.getTitle = function () { 1630
var title 1631
var $e = this.$element 1632
var o = this.options 1633
1634
title = $e.attr('data-original-title') 1635
|| (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) 1636
1637
return title 1638
} 1639
1640
Tooltip.prototype.getUID = function (prefix) { 1641
do prefix += ~~(Math.random() * 1000000) 1642
while (document.getElementById(prefix)) 1643
return prefix 1644
} 1645
1646
Tooltip.prototype.tip = function () { 1647
return (this.$tip = this.$tip || $(this.options.template)) 1648
} 1649
1650
Tooltip.prototype.arrow = function () { 1651
return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')) 1652
} 1653
1654
Tooltip.prototype.enable = function () { 1655
this.enabled = true 1656
} 1657
1658
Tooltip.prototype.disable = function () { 1659
this.enabled = false 1660
} 1661
1662
Tooltip.prototype.toggleEnabled = function () { 1663
this.enabled = !this.enabled 1664
} 1665
1666
Tooltip.prototype.toggle = function (e) { 1667
var self = this 1668
if (e) { 1669
self = $(e.currentTarget).data('bs.' + this.type) 1670
if (!self) { 1671
self = new this.constructor(e.currentTarget, this.getDelegateOptions()) 1672
$(e.currentTarget).data('bs.' + this.type, self) 1673
} 1674
} 1675
1676
self.tip().hasClass('in') ? self.leave(self) : self.enter(self) 1677
} 1678
1679
Tooltip.prototype.destroy = function () { 1680
var that = this 1681
clearTimeout(this.timeout) 1682
this.hide(function () { 1683
that.$element.off('.' + that.type).removeData('bs.' + that.type) 1684
}) 1685
} 1686
1687
1688
// TOOLTIP PLUGIN DEFINITION 1689
// ========================= 1690
1691
function Plugin(option) { 1692
return this.each(function () { 1693
var $this = $(this) 1694
var data = $this.data('bs.tooltip') 1695
var options = typeof option == 'object' && option 1696
1697
if (!data && /destroy|hide/.test(option)) return 1698
if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) 1699
if (typeof option == 'string') data[option]() 1700
}) 1701
} 1702
1703
var old = $.fn.tooltip 1704
1705
$.fn.tooltip = Plugin 1706
$.fn.tooltip.Constructor = Tooltip 1707
1708
1709
// TOOLTIP NO CONFLICT 1710
// =================== 1711
1712
$.fn.tooltip.noConflict = function () { 1713
$.fn.tooltip = old 1714
return this 1715
} 1716
1717
}(jQuery); 1718
1719
/* ======================================================================== 1720
* Bootstrap: popover.js v3.3.4 1721
* http://getbootstrap.com/javascript/#popovers 1722
* ======================================================================== 1723
* Copyright 2011-2015 Twitter, Inc. 1724
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 1725
* ======================================================================== */ 1726
1727
1728
+function ($) { 1729
'use strict'; 1730
1731
// POPOVER PUBLIC CLASS DEFINITION 1732
// =============================== 1733
1734
var Popover = function (element, options) { 1735
this.init('popover', element, options) 1736
} 1737
1738
if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js') 1739
1740
static/js/angular-contenteditable.js View file @ f60d569
File was created 1 /**
2 * @see http://docs.angularjs.org/guide/concepts
3 * @see http://docs.angularjs.org/api/ng.directive:ngModel.NgModelController
4 * @see https://github.com/angular/angular.js/issues/528#issuecomment-7573166
5 */
6
7 angular.module('contenteditable', [])
8 .directive('contenteditable', ['$timeout', function($timeout) { return {
9 restrict: 'A',
10 require: '?ngModel',
11 link: function(scope, element, attrs, ngModel) {
12 // don't do anything unless this is actually bound to a model
13 if (!ngModel) {
14 return
15 }
16
17 // options
18 var opts = {}
19 angular.forEach([
20 'stripBr',
21 'noLineBreaks',
22 'selectNonEditable',
23 'moveCaretToEndOnChange',
24 ], function(opt) {
25 var o = attrs[opt]
26 opts[opt] = o && o !== 'false'
27 })
28
29 // view -> model
30 element.bind('input', function(e) {
31 scope.$apply(function() {
32 var html, html2, rerender
33 html = element.html()
34 rerender = false
35 if (opts.stripBr) {
36 html = html.replace(/<br>$/, '')
37 }
38 if (opts.noLineBreaks) {
39 html2 = html.replace(/<div>/g, '').replace(/<br>/g, '').replace(/<\/div>/g, '')
40 if (html2 !== html) {
41 rerender = true
42 html = html2
43 }
44 }
45 ngModel.$setViewValue(html)
46 if (rerender) {
47 ngModel.$render()
48 }
49 if (html === '') {
50 // the cursor disappears if the contents is empty
51 // so we need to refocus
52 $timeout(function(){
53 element[0].blur()
54 element[0].focus()
55 })
56 }
57 })
58 })
59
60 // model -> view
61 var oldRender = ngModel.$render
62 ngModel.$render = function() {
63 var el, el2, range, sel
64 if (!!oldRender) {
65 oldRender()
66 }
67 element.html(ngModel.$viewValue || '')
68 if (opts.moveCaretToEndOnChange) {
69 el = element[0]
70 range = document.createRange()
71 sel = window.getSelection()
72 if (el.childNodes.length > 0) {
73 el2 = el.childNodes[el.childNodes.length - 1]
74 range.setStartAfter(el2)
75 } else {
76 range.setStartAfter(el)
77 }
78 range.collapse(true)
79 sel.removeAllRanges()
80 sel.addRange(range)
81 }
82 }
83 if (opts.selectNonEditable) {
84 element.bind('click', function(e) {
85 var range, sel, target
86 target = e.toElement
87 if (target !== this && angular.element(target).attr('contenteditable') === 'false') {
88 range = document.createRange()
89 sel = window.getSelection()
90 range.setStartBefore(target)
91 range.setEndAfter(target)
92 sel.removeAllRanges()
93 sel.addRange(range)
94 }
static/js/angular-websocket.js View file @ f60d569
File was created 1 (function() {
2 'use strict';
3
4 var noop = angular.noop;
5 var objectFreeze = (Object.freeze) ? Object.freeze : noop;
6 var objectDefineProperty = Object.defineProperty;
7 var isString = angular.isString;
8 var isFunction = angular.isFunction;
9 var isDefined = angular.isDefined;
10 var isObject = angular.isObject;
11 var isArray = angular.isArray;
12 var forEach = angular.forEach;
13 var arraySlice = Array.prototype.slice;
14 // ie8 wat
15 if (!Array.prototype.indexOf) {
16 Array.prototype.indexOf = function(elt /*, from*/) {
17 var len = this.length >>> 0;
18 var from = Number(arguments[1]) || 0;
19 from = (from < 0) ? Math.ceil(from) : Math.floor(from);
20 if (from < 0) {
21 from += len;
22 }
23
24 for (; from < len; from++) {
25 if (from in this && this[from] === elt) { return from; }
26 }
27 return -1;
28 };
29 }
30
31 // $WebSocketProvider.$inject = ['$rootScope', '$q', '$timeout', '$websocketBackend'];
32 function $WebSocketProvider($rootScope, $q, $timeout, $websocketBackend) {
33
34 function $WebSocket(url, protocols, options) {
35 if (!options && isObject(protocols) && !isArray(protocols)) {
36 options = protocols;
37 protocols = undefined;
38 }
39
40 this.protocols = protocols;
41 this.url = url || 'Missing URL';
42 this.ssl = /(wss)/i.test(this.url);
43
44 // this.binaryType = '';
45 // this.extensions = '';
46 // this.bufferedAmount = 0;
47 // this.trasnmitting = false;
48 // this.buffer = [];
49
50 // TODO: refactor options to use isDefined
51 this.scope = options && options.scope || $rootScope;
52 this.rootScopeFailover = options && options.rootScopeFailover && true;
53 this.useApplyAsync = options && options.useApplyAsync || false;
54 this.initialTimeout = options && options.initialTimeout || 500; // 500ms
55 this.maxTimeout = options && options.maxTimeout || 5 * 60 * 1000; // 5 minutes
56 this.reconnectIfNotNormalClose = options && options.reconnectIfNotNormalClose || false;
57
58 this._reconnectAttempts = 0;
59 this.sendQueue = [];
60 this.onOpenCallbacks = [];
61 this.onMessageCallbacks = [];
62 this.onErrorCallbacks = [];
63 this.onCloseCallbacks = [];
64
65 objectFreeze(this._readyStateConstants);
66
67 if (url) {
68 this._connect();
69 } else {
70 this._setInternalState(0);
71 }
72
73 }
74
75
76 $WebSocket.prototype._readyStateConstants = {
77 'CONNECTING': 0,
78 'OPEN': 1,
79 'CLOSING': 2,
80 'CLOSED': 3,
81 'RECONNECT_ABORTED': 4
82 };
83
84 $WebSocket.prototype._normalCloseCode = 1000;
85
86 $WebSocket.prototype._reconnectableStatusCodes = [
87 4000
88 ];
89
90 $WebSocket.prototype.safeDigest = function safeDigest(autoApply) {
91 if (autoApply && !this.scope.$$phase) {
92 this.scope.$digest();
93 }
94 };
95
96 $WebSocket.prototype.bindToScope = function bindToScope(scope) {
97 var self = this;
98 if (scope) {
99 this.scope = scope;
100 if (this.rootScopeFailover) {
101 this.scope.$on('$destroy', function() {
102 self.scope = $rootScope;
103 });
104 }
105 }
106 return self;
107 };
108
109 $WebSocket.prototype._connect = function _connect(force) {
110 if (force || !this.socket || this.socket.readyState !== this._readyStateConstants.OPEN) {
111 this.socket = $websocketBackend.create(this.url, this.protocols);
112 this.socket.onmessage = angular.bind(this, this._onMessageHandler);
113 this.socket.onopen = angular.bind(this, this._onOpenHandler);
114 this.socket.onerror = angular.bind(this, this._onErrorHandler);
115 this.socket.onclose = angular.bind(this, this._onCloseHandler);
116 }
117 };
118
119 $WebSocket.prototype.fireQueue = function fireQueue() {
120 while (this.sendQueue.length && this.socket.readyState === this._readyStateConstants.OPEN) {
121 var data = this.sendQueue.shift();
122
123 this.socket.send(
124 isString(data.message) ? data.message : JSON.stringify(data.message)
125 );
126 data.deferred.resolve();
127 }
128 };
129
130 $WebSocket.prototype.notifyOpenCallbacks = function notifyOpenCallbacks(event) {
131 for (var i = 0; i < this.onOpenCallbacks.length; i++) {
132 this.onOpenCallbacks[i].call(this, event);
133 }
134 };
135
136 $WebSocket.prototype.notifyCloseCallbacks = function notifyCloseCallbacks(event) {
137 for (var i = 0; i < this.onCloseCallbacks.length; i++) {
138 this.onCloseCallbacks[i].call(this, event);
139 }
140 };
141
142 $WebSocket.prototype.notifyErrorCallbacks = function notifyErrorCallbacks(event) {
143 for (var i = 0; i < this.onErrorCallbacks.length; i++) {
144 this.onErrorCallbacks[i].call(this, event);
145 }
146 };
147
148 $WebSocket.prototype.onOpen = function onOpen(cb) {
149 this.onOpenCallbacks.push(cb);
150 return this;
151 };
152
153 $WebSocket.prototype.onClose = function onClose(cb) {
154 this.onCloseCallbacks.push(cb);
155 return this;
156 };
157
158 $WebSocket.prototype.onError = function onError(cb) {
159 this.onErrorCallbacks.push(cb);
160 return this;
161 };
162
163
164 $WebSocket.prototype.onMessage = function onMessage(callback, options) {
165 if (!isFunction(callback)) {
166 throw new Error('Callback must be a function');
167 }
168
169 if (options && isDefined(options.filter) && !isString(options.filter) && !(options.filter instanceof RegExp)) {
170 throw new Error('Pattern must be a string or regular expression');
171 }
172
173 this.onMessageCallbacks.push({
174 fn: callback,
175 pattern: options ? options.filter : undefined,
176 autoApply: options ? options.autoApply : true
177 });
178 return this;
179 };
180
181 $WebSocket.prototype._onOpenHandler = function _onOpenHandler(event) {
182 this._reconnectAttempts = 0;
183 this.notifyOpenCallbacks(event);
184 this.fireQueue();
185 };
186
187 $WebSocket.prototype._onCloseHandler = function _onCloseHandler(event) {
188 this.notifyCloseCallbacks(event);
189 if ((this.reconnectIfNotNormalClose && event.code !== this._normalCloseCode) || this._reconnectableStatusCodes.indexOf(event.code) > -1) {
190 this.reconnect();
191 }
192 };
193
194 $WebSocket.prototype._onErrorHandler = function _onErrorHandler(event) {
195 this.notifyErrorCallbacks(event);
196 };
197
198 $WebSocket.prototype._onMessageHandler = function _onMessageHandler(message) {
199 var pattern;
200 var self = this;
201 var currentCallback;
202 for (var i = 0; i < self.onMessageCallbacks.length; i++) {
203 currentCallback = self.onMessageCallbacks[i];
204 pattern = currentCallback.pattern;
205 if (pattern) {
206 if (isString(pattern) && message.data === pattern) {
207 applyAsyncOrDigest(currentCallback.fn, currentCallback.autoApply, message);
208 }
209 else if (pattern instanceof RegExp && pattern.exec(message.data)) {
210 applyAsyncOrDigest(currentCallback.fn, currentCallback.autoApply, message);
211 }
212 }
213 else {
214 applyAsyncOrDigest(currentCallback.fn, currentCallback.autoApply, message);
215 }
216 }
217
218 function applyAsyncOrDigest(callback, autoApply, args) {
219 args = arraySlice.call(arguments, 2);
220 if (self.useApplyAsync) {
221 self.scope.$applyAsync(function() {
222 callback.apply(self, args);
223 });
224 } else {
225 callback.apply(self, args);
226 self.safeDigest(autoApply);
227 }
228 }
229
230 };
231
232 $WebSocket.prototype.close = function close(force) {
233 if (force || !this.socket.bufferedAmount) {
234 this.socket.close();
235 }
236 return this;
237 };
238
239 $WebSocket.prototype.send = function send(data) {
240 var deferred = $q.defer();
241 var self = this;
242 var promise = cancelableify(deferred.promise);
243
244 if (self.readyState === self._readyStateConstants.RECONNECT_ABORTED) {
245 deferred.reject('Socket connection has been closed');
246 }
247 else {
248 self.sendQueue.push({
249 message: data,
250 deferred: deferred
251 });
252 self.fireQueue();
253 }
254
255 // Credit goes to @btford
256 function cancelableify(promise) {
257 promise.cancel = cancel;
258 var then = promise.then;
259 promise.then = function() {
260 var newPromise = then.apply(this, arguments);
261 return cancelableify(newPromise);
262 };
263 return promise;
264 }
265
266 function cancel(reason) {
267 self.sendQueue.splice(self.sendQueue.indexOf(data), 1);
268 deferred.reject(reason);
269 return self;
270 }
271
272 if ($websocketBackend.isMocked && $websocketBackend.isMocked() &&
273 $websocketBackend.isConnected(this.url)) {
274 this._onMessageHandler($websocketBackend.mockSend());
275 }
276
277 return promise;
278 };
279
280 $WebSocket.prototype.reconnect = function reconnect() {
281 this.close();
282
283 var backoffDelay = this._getBackoffDelay(++this._reconnectAttempts);
284
285 var backoffDelaySeconds = backoffDelay / 1000;
286 console.log('Reconnecting in ' + backoffDelaySeconds + ' seconds');
287
288 $timeout(angular.bind(this, this._connect), backoffDelay);
289
290 return this;
291 };
292 // Exponential Backoff Formula by Prof. Douglas Thain
293 // http://dthain.blogspot.co.uk/2009/02/exponential-backoff-in-distributed.html
294 $WebSocket.prototype._getBackoffDelay = function _getBackoffDelay(attempt) {
295 var R = Math.random() + 1;
296 var T = this.initialTimeout;
297 var F = 2;
298 var N = attempt;
299 var M = this.maxTimeout;
300
301 return Math.floor(Math.min(R * T * Math.pow(F, N), M));
302 };
303
304 $WebSocket.prototype._setInternalState = function _setInternalState(state) {
305 if (Math.floor(state) !== state || state < 0 || state > 4) {
306 throw new Error('state must be an integer between 0 and 4, got: ' + state);
307 }
308
309 // ie8 wat
310 if (!objectDefineProperty) {
311 this.readyState = state || this.socket.readyState;
312 }
313 this._internalConnectionState = state;
314
315
316 forEach(this.sendQueue, function(pending) {
317 pending.deferred.reject('Message cancelled due to closed socket connection');
318 });

396 Bytes

ws_debug.html View file @ f60d569
<!DOCTYPE html> 1 File was deleted
<html> 2
<head lang="en"> 3
<meta charset="UTF-8"> 4
<title></title> 5
</head> 6
<body> 7
<ul> 8
9
</ul> 10
</body> 11
<script src="//code.jquery.com/jquery-2.1.4.min.js"></script> 12
<script> 13
var loc = window.location, new_uri; 14
if (loc.protocol === "https:") { 15
new_uri = "wss:"; 16
} else { 17
new_uri = "ws:"; 18
} 19
new_uri += "//" + loc.host; 20
var ws = new WebSocket(new_uri + '/ws/deck/496?subscribe-user'); 21
ws.onopen = function () { 22
console.log("websocket connected"); 23
}; 24
ws.onmessage = function (e) { 25
$('ul').append('<li>' + e.data + '</li>'); 26
}; 27
ws.onerror = function (e) { 28
console.error(e); 29
}; 30
ws.onclose = function (e) { 31
console.log("connection closed"); 32
}; 33
function send_message(msg) {1 34
ws.send(msg); 35
} 36
var ws = new WebSocket(new_uri + '/ws/feed/496?subscribe-broadcast'); 37
ws.onopen = function () { 38
console.log("websocket connected"); 39
}; 40
ws.onmessage = function (e) { 41
$('ul').append('<li>' + e.data + '</li>'); 42
}; 43
ws.onerror = function (e) { 44
console.error(e); 45
}; 46
ws.onclose = function (e) { 47
console.log("connection closed"); 48
}; 49
function send_message(msg) { 50
ws.send(msg); 51
} 52
</script> 53