mirror of
https://github.com/mmumshad/ansible-playable.git
synced 2025-03-09 23:38:54 +00:00
Initial Commit
This commit is contained in:
commit
c92f737237
273 changed files with 16964 additions and 0 deletions
34
client/components/auth/auth.module.js
Normal file
34
client/components/auth/auth.module.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
'use strict';
|
||||
|
||||
import angular from 'angular';
|
||||
import constants from '../../app/app.constants';
|
||||
import util from '../util/util.module';
|
||||
import ngCookies from 'angular-cookies';
|
||||
import {
|
||||
authInterceptor
|
||||
} from './interceptor.service';
|
||||
import {
|
||||
routerDecorator
|
||||
} from './router.decorator';
|
||||
import {
|
||||
AuthService
|
||||
} from './auth.service';
|
||||
import {
|
||||
UserResource
|
||||
} from './user.service';
|
||||
|
||||
import uiRouter from 'angular-ui-router';
|
||||
|
||||
function addInterceptor($httpProvider) {
|
||||
'ngInject';
|
||||
|
||||
$httpProvider.interceptors.push('authInterceptor');
|
||||
}
|
||||
|
||||
export default angular.module('app2App.auth', [constants, util, ngCookies, uiRouter])
|
||||
.factory('authInterceptor', authInterceptor)
|
||||
.run(routerDecorator)
|
||||
.factory('Auth', AuthService)
|
||||
.factory('User', UserResource)
|
||||
.config(['$httpProvider', addInterceptor])
|
||||
.name;
|
226
client/components/auth/auth.service.js
Normal file
226
client/components/auth/auth.service.js
Normal file
|
@ -0,0 +1,226 @@
|
|||
'use strict';
|
||||
|
||||
import * as _ from 'lodash';
|
||||
|
||||
|
||||
class _User {
|
||||
_id = '';
|
||||
name = '';
|
||||
email = '';
|
||||
role = '';
|
||||
$promise = undefined;
|
||||
}
|
||||
|
||||
export function AuthService($location, $http, $cookies, $q, appConfig, Util, User) {
|
||||
'ngInject';
|
||||
|
||||
var safeCb = Util.safeCb;
|
||||
var currentUser = new _User();
|
||||
var userRoles = appConfig.userRoles || [];
|
||||
/**
|
||||
* Check if userRole is >= role
|
||||
* @param {String} userRole - role of current user
|
||||
* @param {String} role - role to check against
|
||||
*/
|
||||
var hasRole = function(userRole, role) {
|
||||
return userRoles.indexOf(userRole) >= userRoles.indexOf(role);
|
||||
};
|
||||
|
||||
if($cookies.get('token') && $location.path() !== '/logout') {
|
||||
currentUser = User.get();
|
||||
}
|
||||
|
||||
var Auth = {
|
||||
/**
|
||||
* Authenticate user and save token
|
||||
*
|
||||
* @param {Object} user - login info
|
||||
* @param {Function} callback - function(error, user)
|
||||
* @return {Promise}
|
||||
*/
|
||||
login({
|
||||
email,
|
||||
password
|
||||
}, callback) {
|
||||
return $http.post('/auth/local', {
|
||||
email,
|
||||
password
|
||||
})
|
||||
.then(res => {
|
||||
$cookies.put('token', res.data.token);
|
||||
currentUser = User.get();
|
||||
return currentUser.$promise;
|
||||
})
|
||||
.then(user => {
|
||||
safeCb(callback)(null, user);
|
||||
return user;
|
||||
})
|
||||
.catch(err => {
|
||||
Auth.logout();
|
||||
safeCb(callback)(err.data);
|
||||
return $q.reject(err.data);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete access token and user info
|
||||
*/
|
||||
logout() {
|
||||
$cookies.remove('token');
|
||||
currentUser = new _User();
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a new user
|
||||
*
|
||||
* @param {Object} user - user info
|
||||
* @param {Function} callback - function(error, user)
|
||||
* @return {Promise}
|
||||
*/
|
||||
createUser(user, callback) {
|
||||
return User.save(user, function(data) {
|
||||
$cookies.put('token', data.token);
|
||||
currentUser = User.get();
|
||||
return safeCb(callback)(null, user);
|
||||
}, function(err) {
|
||||
Auth.logout();
|
||||
return safeCb(callback)(err);
|
||||
})
|
||||
.$promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Change password
|
||||
*
|
||||
* @param {String} oldPassword
|
||||
* @param {String} newPassword
|
||||
* @param {Function} callback - function(error, user)
|
||||
* @return {Promise}
|
||||
*/
|
||||
changePassword(oldPassword, newPassword, callback) {
|
||||
return User.changePassword({
|
||||
id: currentUser._id
|
||||
}, {
|
||||
oldPassword,
|
||||
newPassword
|
||||
}, function() {
|
||||
return safeCb(callback)(null);
|
||||
}, function(err) {
|
||||
return safeCb(callback)(err);
|
||||
})
|
||||
.$promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets all available info on a user
|
||||
*
|
||||
* @param {Function} [callback] - function(user)
|
||||
* @return {Promise}
|
||||
*/
|
||||
getCurrentUser(callback) {
|
||||
var value = _.get(currentUser, '$promise') ? currentUser.$promise : currentUser;
|
||||
|
||||
return $q.when(value)
|
||||
.then(user => {
|
||||
safeCb(callback)(user);
|
||||
return user;
|
||||
}, () => {
|
||||
safeCb(callback)({});
|
||||
return {};
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets all available info on a user
|
||||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
getCurrentUserSync() {
|
||||
return currentUser;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if a user is logged in
|
||||
*
|
||||
* @param {Function} [callback] - function(is)
|
||||
* @return {Promise}
|
||||
*/
|
||||
isLoggedIn(callback) {
|
||||
return Auth.getCurrentUser(undefined)
|
||||
.then(user => {
|
||||
let is = _.get(user, 'role');
|
||||
|
||||
safeCb(callback)(is);
|
||||
return is;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if a user is logged in
|
||||
*
|
||||
* @return {Bool}
|
||||
*/
|
||||
isLoggedInSync() {
|
||||
return !!_.get(currentUser, 'role');
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if a user has a specified role or higher
|
||||
*
|
||||
* @param {String} role - the role to check against
|
||||
* @param {Function} [callback] - function(has)
|
||||
* @return {Promise}
|
||||
*/
|
||||
hasRole(role, callback) {
|
||||
return Auth.getCurrentUser(undefined)
|
||||
.then(user => {
|
||||
let has = hasRole(_.get(user, 'role'), role);
|
||||
|
||||
safeCb(callback)(has);
|
||||
return has;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if a user has a specified role or higher
|
||||
*
|
||||
* @param {String} role - the role to check against
|
||||
* @return {Bool}
|
||||
*/
|
||||
hasRoleSync(role) {
|
||||
return hasRole(_.get(currentUser, 'role'), role);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if a user is an admin
|
||||
* (synchronous|asynchronous)
|
||||
*
|
||||
* @param {Function|*} callback - optional, function(is)
|
||||
* @return {Bool|Promise}
|
||||
*/
|
||||
isAdmin(...args) {
|
||||
return Auth.hasRole(Reflect.apply([].concat, ['admin'], args));
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if a user is an admin
|
||||
*
|
||||
* @return {Bool}
|
||||
*/
|
||||
isAdminSync() {
|
||||
// eslint-disable-next-line no-sync
|
||||
return Auth.hasRoleSync('admin');
|
||||
},
|
||||
|
||||
/**
|
||||
* Get auth token
|
||||
*
|
||||
* @return {String} - a token string used for authenticating
|
||||
*/
|
||||
getToken() {
|
||||
return $cookies.get('token');
|
||||
}
|
||||
};
|
||||
|
||||
return Auth;
|
||||
}
|
28
client/components/auth/interceptor.service.js
Normal file
28
client/components/auth/interceptor.service.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
'use strict';
|
||||
|
||||
export function authInterceptor($rootScope, $q, $cookies, $injector, Util) {
|
||||
'ngInject';
|
||||
|
||||
var state;
|
||||
return {
|
||||
// Add authorization token to headers
|
||||
request(config) {
|
||||
config.headers = config.headers || {};
|
||||
if($cookies.get('token') && Util.isSameOrigin(config.url)) {
|
||||
config.headers.Authorization = `Bearer ${$cookies.get('token')}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
|
||||
// Intercept 401s and redirect you to login
|
||||
responseError(response) {
|
||||
if(response.status === 401) {
|
||||
(state || (state = $injector.get('$state')))
|
||||
.go('login');
|
||||
// remove any stale tokens
|
||||
$cookies.remove('token');
|
||||
}
|
||||
return $q.reject(response);
|
||||
}
|
||||
};
|
||||
}
|
38
client/components/auth/router.decorator.js
Normal file
38
client/components/auth/router.decorator.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
'use strict';
|
||||
|
||||
export function routerDecorator($rootScope, $state, Auth) {
|
||||
'ngInject';
|
||||
// Redirect to login if route requires auth and the user is not logged in, or doesn't have required role
|
||||
|
||||
$rootScope.$on('$stateChangeStart', function(event, next) {
|
||||
if(!next.authenticate) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof next.authenticate === 'string') {
|
||||
Auth.hasRole(next.authenticate)
|
||||
.then(has => {
|
||||
if(has) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
return Auth.isLoggedIn()
|
||||
.then(is => {
|
||||
$state.go(is ? 'main' : 'login');
|
||||
});
|
||||
});
|
||||
} else {
|
||||
Auth.isLoggedIn()
|
||||
.then(is => {
|
||||
if(is) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
$state.go('login');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
22
client/components/auth/user.service.js
Normal file
22
client/components/auth/user.service.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
'use strict';
|
||||
|
||||
export function UserResource($resource) {
|
||||
'ngInject';
|
||||
|
||||
return $resource('/api/users/:id/:controller', {
|
||||
id: '@_id'
|
||||
}, {
|
||||
changePassword: {
|
||||
method: 'PUT',
|
||||
params: {
|
||||
controller: 'password'
|
||||
}
|
||||
},
|
||||
get: {
|
||||
method: 'GET',
|
||||
params: {
|
||||
id: 'me'
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
10
client/components/footer/footer.component.js
Normal file
10
client/components/footer/footer.component.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
import angular from 'angular';
|
||||
|
||||
export class FooterComponent {}
|
||||
|
||||
export default angular.module('directives.footer', [])
|
||||
.component('footer', {
|
||||
template: require('./footer.html'),
|
||||
controller: FooterComponent
|
||||
})
|
||||
.name;
|
6
client/components/footer/footer.css
Normal file
6
client/components/footer/footer.css
Normal file
|
@ -0,0 +1,6 @@
|
|||
footer.footer {
|
||||
text-align: center;
|
||||
padding: 30px 0;
|
||||
margin-top: 70px;
|
||||
border-top: 1px solid #E5E5E5;
|
||||
}
|
8
client/components/footer/footer.html
Normal file
8
client/components/footer/footer.html
Normal file
|
@ -0,0 +1,8 @@
|
|||
<div class="container">
|
||||
<p>Angular Fullstack v4.2.2 |
|
||||
<a href="http://angular-fullstack.github.io/">Documentation</a> |
|
||||
<a href="https://gitter.im/DaftMonk/generator-angular-fullstack">Chat on Gitter</a> |
|
||||
<a href="https://github.com/angular-fullstack/generator-angular-fullstack/issues?state=open">Issues</a> |
|
||||
<a href="https://opencollective.com/angular-fullstack">Donate to our Open Collective</a>
|
||||
</p>
|
||||
</div>
|
23
client/components/modal/modal.css
Normal file
23
client/components/modal/modal.css
Normal file
|
@ -0,0 +1,23 @@
|
|||
.modal-primary .modal-header,
|
||||
.modal-info .modal-header,
|
||||
.modal-success .modal-header,
|
||||
.modal-warning .modal-header,
|
||||
.modal-danger .modal-header {
|
||||
color: #fff;
|
||||
border-radius: 5px 5px 0 0;
|
||||
}
|
||||
.modal-primary .modal-header {
|
||||
background: #428bca;
|
||||
}
|
||||
.modal-info .modal-header {
|
||||
background: #5bc0de;
|
||||
}
|
||||
.modal-success .modal-header {
|
||||
background: #5cb85c;
|
||||
}
|
||||
.modal-warning .modal-header {
|
||||
background: #f0ad4e;
|
||||
}
|
||||
.modal-danger .modal-header {
|
||||
background: #d9534f;
|
||||
}
|
11
client/components/modal/modal.html
Normal file
11
client/components/modal/modal.html
Normal file
|
@ -0,0 +1,11 @@
|
|||
<div class="modal-header">
|
||||
<button ng-if="modal.dismissable" type="button" ng-click="$dismiss()" class="close">×</button>
|
||||
<h4 ng-if="modal.title" ng-bind="modal.title" class="modal-title"></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p ng-if="modal.text" ng-bind="modal.text"></p>
|
||||
<div ng-if="modal.html" ng-bind-html="modal.html"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button ng-repeat="button in modal.buttons" ng-class="button.classes" ng-click="button.click($event)" ng-bind="button.text" class="btn"></button>
|
||||
</div>
|
78
client/components/modal/modal.service.js
Normal file
78
client/components/modal/modal.service.js
Normal file
|
@ -0,0 +1,78 @@
|
|||
'use strict';
|
||||
|
||||
import angular from 'angular';
|
||||
|
||||
export function Modal($rootScope, $uibModal) {
|
||||
/**
|
||||
* Opens a modal
|
||||
* @param {Object} scope - an object to be merged with modal's scope
|
||||
* @param {String} modalClass - (optional) class(es) to be applied to the modal
|
||||
* @return {Object} - the instance $uibModal.open() returns
|
||||
*/
|
||||
function openModal(scope = {}, modalClass = 'modal-default') {
|
||||
var modalScope = $rootScope.$new();
|
||||
|
||||
angular.extend(modalScope, scope);
|
||||
|
||||
return $uibModal.open({
|
||||
template: require('./modal.html'),
|
||||
windowClass: modalClass,
|
||||
scope: modalScope
|
||||
});
|
||||
}
|
||||
|
||||
// Public API here
|
||||
return {
|
||||
|
||||
/* Confirmation modals */
|
||||
confirm: {
|
||||
|
||||
/**
|
||||
* Create a function to open a delete confirmation modal (ex. ng-click='myModalFn(name, arg1, arg2...)')
|
||||
* @param {Function} del - callback, ran when delete is confirmed
|
||||
* @return {Function} - the function to open the modal (ex. myModalFn)
|
||||
*/
|
||||
delete(del = angular.noop) {
|
||||
/**
|
||||
* Open a delete confirmation modal
|
||||
* @param {String} name - name or info to show on modal
|
||||
* @param {All} - any additional args are passed straight to del callback
|
||||
*/
|
||||
return function(...args) {
|
||||
var slicedArgs = Reflect.apply(Array.prototype.slice, args);
|
||||
var name = slicedArgs.shift();
|
||||
var deleteModal;
|
||||
|
||||
deleteModal = openModal({
|
||||
modal: {
|
||||
dismissable: true,
|
||||
title: 'Confirm Delete',
|
||||
html: `<p>Are you sure you want to delete <strong>${name}</strong> ?</p>`,
|
||||
buttons: [{
|
||||
classes: 'btn-danger',
|
||||
text: 'Delete',
|
||||
click(e) {
|
||||
deleteModal.close(e);
|
||||
}
|
||||
}, {
|
||||
classes: 'btn-default',
|
||||
text: 'Cancel',
|
||||
click(e) {
|
||||
deleteModal.dismiss(e);
|
||||
}
|
||||
}]
|
||||
}
|
||||
}, 'modal-danger');
|
||||
|
||||
deleteModal.result.then(function(event) {
|
||||
Reflect.apply(del, event, slicedArgs);
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default angular.module('app2App.Modal', [])
|
||||
.factory('Modal', Modal)
|
||||
.name;
|
17
client/components/mongoose-error/mongoose-error.directive.js
Normal file
17
client/components/mongoose-error/mongoose-error.directive.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
'use strict';
|
||||
|
||||
import angular from 'angular';
|
||||
|
||||
/**
|
||||
* Removes server error when user updates input
|
||||
*/
|
||||
angular.module('app2App')
|
||||
.directive('mongooseError', function() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: 'ngModel',
|
||||
link(scope, element, attrs, ngModel) {
|
||||
element.on('keydown', () => ngModel.$setValidity('mongoose', true));
|
||||
}
|
||||
};
|
||||
});
|
41
client/components/navbar/navbar.component.js
Normal file
41
client/components/navbar/navbar.component.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
'use strict';
|
||||
/* eslint no-sync: 0 */
|
||||
|
||||
import angular from 'angular';
|
||||
|
||||
export class NavbarComponent {
|
||||
menu = [{
|
||||
'title': 'Home',
|
||||
'state': 'main'
|
||||
},{
|
||||
'title': 'Projects',
|
||||
'state': 'project'
|
||||
},{
|
||||
'title': 'Designer',
|
||||
'state': 'designer'
|
||||
},{
|
||||
'title': 'Runs',
|
||||
'state': 'runs'
|
||||
},{
|
||||
'title': 'Modules',
|
||||
'state': 'custom_modules'
|
||||
}];
|
||||
|
||||
isCollapsed = true;
|
||||
|
||||
constructor(Auth) {
|
||||
'ngInject';
|
||||
|
||||
this.isLoggedIn = Auth.isLoggedInSync;
|
||||
this.isAdmin = Auth.isAdminSync;
|
||||
this.getCurrentUser = Auth.getCurrentUserSync;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default angular.module('directives.navbar', [])
|
||||
.component('navbar', {
|
||||
template: require('./navbar.html'),
|
||||
controller: NavbarComponent
|
||||
})
|
||||
.name;
|
29
client/components/navbar/navbar.html
Normal file
29
client/components/navbar/navbar.html
Normal file
|
@ -0,0 +1,29 @@
|
|||
<div class="navbar navbar-default navbar-static-top">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<a href="/" class="navbar-brand"><small><span style="font-size:25px;font-family:'ExpletusSans-Regular'">play<span style="font-size:35px;">A</span>ble</span></small></a>
|
||||
<button class="navbar-toggle" type="button" ng-click="$ctrl.isCollapsed = !$ctrl.isCollapsed">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div uib-collapse="$ctrl.isCollapsed" class="navbar-collapse collapse" id="navbar-main">
|
||||
<ul class="nav navbar-nav">
|
||||
<li ng-repeat="item in $ctrl.menu" ui-sref-active="active">
|
||||
<a ui-sref="{{item.state}}">{{item.title}}</a>
|
||||
</li>
|
||||
<li ng-show="$ctrl.isAdmin()" ui-sref-active="active"><a ui-sref="admin">Admin</a></li>
|
||||
</ul>
|
||||
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ng-hide="$ctrl.isLoggedIn()" ui-sref-active="active"><a ui-sref="signup">Sign up</a></li>
|
||||
<li ng-hide="$ctrl.isLoggedIn()" ui-sref-active="active"><a ui-sref="login">Login</a></li>
|
||||
<li ng-show="$ctrl.isLoggedIn()"><p class="navbar-text">Hello {{ $ctrl.getCurrentUser().name }}</p> </li>
|
||||
<li ng-show="$ctrl.isLoggedIn()" ui-sref-active="active"><a ui-sref="settings"><span class="glyphicon glyphicon-cog"></span></a></li>
|
||||
<li ng-show="$ctrl.isLoggedIn()"><a ui-sref="logout">Logout</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
25
client/components/oauth-buttons/index.js
Normal file
25
client/components/oauth-buttons/index.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
'use strict';
|
||||
|
||||
import angular from 'angular';
|
||||
|
||||
export function OauthButtonsController($window) {
|
||||
'ngInject';
|
||||
|
||||
this.loginOauth = function(provider) {
|
||||
$window.location.href = `/auth/${provider}`;
|
||||
};
|
||||
}
|
||||
|
||||
export default angular.module('app2App.oauthButtons', [])
|
||||
.directive('oauthButtons', function() {
|
||||
return {
|
||||
template: require('./oauth-buttons.html'),
|
||||
restrict: 'EA',
|
||||
controller: OauthButtonsController,
|
||||
controllerAs: 'OauthButtons',
|
||||
scope: {
|
||||
classes: '@'
|
||||
}
|
||||
};
|
||||
})
|
||||
.name;
|
|
@ -0,0 +1,32 @@
|
|||
'use strict';
|
||||
|
||||
import {
|
||||
OauthButtonsController
|
||||
} from './index';
|
||||
|
||||
describe('Controller: OauthButtonsController', function() {
|
||||
var controller, $window;
|
||||
|
||||
beforeEach(() => {
|
||||
angular.module('test', [])
|
||||
.controller('OauthButtonsController', OauthButtonsController);
|
||||
});
|
||||
// load the controller's module
|
||||
beforeEach(angular.mock.module('test'));
|
||||
|
||||
// Initialize the controller and a mock $window
|
||||
beforeEach(inject(function($controller) {
|
||||
$window = {
|
||||
location: {}
|
||||
};
|
||||
|
||||
controller = $controller('OauthButtonsController', {
|
||||
$window
|
||||
});
|
||||
}));
|
||||
|
||||
it('should attach loginOauth', function() {
|
||||
expect(controller.loginOauth)
|
||||
.to.be.a('function');
|
||||
});
|
||||
});
|
1
client/components/oauth-buttons/oauth-buttons.css
Normal file
1
client/components/oauth-buttons/oauth-buttons.css
Normal file
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
'use strict';
|
||||
|
||||
const $ = require('sprint-js');
|
||||
import OauthButtons from './index';
|
||||
|
||||
describe('Directive: oauthButtons', function() {
|
||||
// load the directive's module and view
|
||||
beforeEach(angular.mock.module(OauthButtons));
|
||||
// beforeEach(angular.mock.module('components/oauth-buttons/oauth-buttons.html'));
|
||||
|
||||
var element, parentScope, elementScope;
|
||||
|
||||
var compileDirective = function(template) {
|
||||
inject(function($compile) {
|
||||
element = angular.element(template);
|
||||
element = $compile(element)(parentScope);
|
||||
parentScope.$digest();
|
||||
elementScope = element.isolateScope();
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(inject(function($rootScope) {
|
||||
parentScope = $rootScope.$new();
|
||||
}));
|
||||
|
||||
it('should contain anchor buttons', function() {
|
||||
compileDirective('<oauth-buttons></oauth-buttons>');
|
||||
expect($(element[0])
|
||||
.find('a.btn.btn-social')
|
||||
.length)
|
||||
.to.be.at.least(1);
|
||||
});
|
||||
|
||||
it('should evaluate and bind the classes attribute to scope.classes', function() {
|
||||
parentScope.scopedClass = 'scopedClass1';
|
||||
compileDirective('<oauth-buttons classes="testClass1 {{scopedClass}}"></oauth-buttons>');
|
||||
expect(elementScope.classes)
|
||||
.to.equal('testClass1 scopedClass1');
|
||||
});
|
||||
|
||||
it('should bind scope.classes to class names on the anchor buttons', function() {
|
||||
compileDirective('<oauth-buttons></oauth-buttons>');
|
||||
// Add classes
|
||||
elementScope.classes = 'testClass1 testClass2';
|
||||
elementScope.$digest();
|
||||
expect($(element[0])
|
||||
.find('a.btn.btn-social.testClass1.testClass2')
|
||||
.length)
|
||||
.to.be.at.least(1);
|
||||
|
||||
// Remove classes
|
||||
elementScope.classes = '';
|
||||
elementScope.$digest();
|
||||
expect($(element[0])
|
||||
.find('a.btn.btn-social.testClass1.testClass2')
|
||||
.length)
|
||||
.to.equal(0);
|
||||
});
|
||||
});
|
9
client/components/oauth-buttons/oauth-buttons.html
Normal file
9
client/components/oauth-buttons/oauth-buttons.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
<a ng-class="classes" ng-click="OauthButtons.loginOauth('facebook')" class="btn btn-social btn-facebook">
|
||||
<i class="fa fa-facebook"></i>
|
||||
Connect with Facebook
|
||||
</a>
|
||||
<a ng-class="classes" ng-click="OauthButtons.loginOauth('google')" class="btn btn-social btn-google">
|
||||
<i class="fa fa-google-plus"></i>
|
||||
Connect with Google+
|
||||
</a>
|
||||
|
38
client/components/ui-router/ui-router.mock.js
Normal file
38
client/components/ui-router/ui-router.mock.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
'use strict';
|
||||
|
||||
const angular = require('angular');
|
||||
|
||||
angular.module('stateMock', []);
|
||||
angular.module('stateMock')
|
||||
.service('$state', function($q) {
|
||||
this.expectedTransitions = [];
|
||||
|
||||
this.transitionTo = function(stateName) {
|
||||
if(this.expectedTransitions.length > 0) {
|
||||
var expectedState = this.expectedTransitions.shift();
|
||||
if(expectedState !== stateName) {
|
||||
throw Error(`Expected transition to state: ${expectedState
|
||||
} but transitioned to ${stateName}`);
|
||||
}
|
||||
} else {
|
||||
throw Error(`No more transitions were expected! Tried to transition to ${stateName}`);
|
||||
}
|
||||
console.log(`Mock transition to: ${stateName}`);
|
||||
var deferred = $q.defer();
|
||||
var promise = deferred.promise;
|
||||
deferred.resolve();
|
||||
return promise;
|
||||
};
|
||||
|
||||
this.go = this.transitionTo;
|
||||
|
||||
this.expectTransitionTo = function(stateName) {
|
||||
this.expectedTransitions.push(stateName);
|
||||
};
|
||||
|
||||
this.ensureAllTransitionsHappened = function() {
|
||||
if(this.expectedTransitions.length > 0) {
|
||||
throw Error('Not all transitions happened!');
|
||||
}
|
||||
};
|
||||
});
|
10
client/components/util/util.module.js
Normal file
10
client/components/util/util.module.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
'use strict';
|
||||
|
||||
import angular from 'angular';
|
||||
import {
|
||||
UtilService
|
||||
} from './util.service';
|
||||
|
||||
export default angular.module('app2App.util', [])
|
||||
.factory('Util', UtilService)
|
||||
.name;
|
68
client/components/util/util.service.js
Normal file
68
client/components/util/util.service.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
'use strict';
|
||||
|
||||
import angular from 'angular';
|
||||
|
||||
/**
|
||||
* The Util service is for thin, globally reusable, utility functions
|
||||
*/
|
||||
export function UtilService($window) {
|
||||
'ngInject';
|
||||
|
||||
var Util = {
|
||||
/**
|
||||
* Return a callback or noop function
|
||||
*
|
||||
* @param {Function|*} cb - a 'potential' function
|
||||
* @return {Function}
|
||||
*/
|
||||
safeCb(cb) {
|
||||
return angular.isFunction(cb) ? cb : angular.noop;
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse a given url with the use of an anchor element
|
||||
*
|
||||
* @param {String} url - the url to parse
|
||||
* @return {Object} - the parsed url, anchor element
|
||||
*/
|
||||
urlParse(url) {
|
||||
var a = document.createElement('a');
|
||||
a.href = url;
|
||||
|
||||
// Special treatment for IE, see http://stackoverflow.com/a/13405933 for details
|
||||
if(a.host === '') {
|
||||
a.href = a.href;
|
||||
}
|
||||
|
||||
return a;
|
||||
},
|
||||
|
||||
/**
|
||||
* Test whether or not a given url is same origin
|
||||
*
|
||||
* @param {String} url - url to test
|
||||
* @param {String|String[]} [origins] - additional origins to test against
|
||||
* @return {Boolean} - true if url is same origin
|
||||
*/
|
||||
isSameOrigin(url, origins) {
|
||||
url = Util.urlParse(url);
|
||||
origins = origins && [].concat(origins) || [];
|
||||
origins = origins.map(Util.urlParse);
|
||||
origins.push($window.location);
|
||||
origins = origins.filter(function(o) {
|
||||
let hostnameCheck = url.hostname === o.hostname;
|
||||
let protocolCheck = url.protocol === o.protocol;
|
||||
// 2nd part of the special treatment for IE fix (see above):
|
||||
// This part is when using well-known ports 80 or 443 with IE,
|
||||
// when $window.location.port==='' instead of the real port number.
|
||||
// Probably the same cause as this IE bug: https://goo.gl/J9hRta
|
||||
let portCheck = url.port === o.port || o.port === '' && (url.port === '80' || url.port
|
||||
=== '443');
|
||||
return hostnameCheck && protocolCheck && portCheck;
|
||||
});
|
||||
return origins.length >= 1;
|
||||
}
|
||||
};
|
||||
|
||||
return Util;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue