- added multi character support per user, #314

- improved client site storage (IndexedDB) setup
This commit is contained in:
Exodus4D
2016-09-14 19:51:14 +02:00
parent d3001c2e4d
commit 74faec37c9
14 changed files with 439 additions and 162 deletions

View File

@@ -779,7 +779,7 @@ class Map extends Controller\AccessController {
$systemPosX = 0;
$systemPosY = 30;
$sourceSystemId = (int)$this->getF3()->get(User::SESSION_KEY_CHARACTER_PREV_SYSTEM_ID);
$sourceSystemId = (int)$this->getF3()->get(User::SESSION_KEY_CHARACTERS_PREV_SYSTEM_ID);
$targetSystemId = (int)$log->systemId;
if($sourceSystemId){

View File

@@ -25,14 +25,12 @@ class User extends Controller\Controller{
const SESSION_KEY_USER_NAME = 'SESSION.USER.NAME';
// character specific session keys
const SESSION_KEY_CHARACTER = 'SESSION.CHARACTER';
const SESSION_KEY_CHARACTER_ID = 'SESSION.CHARACTER.ID';
const SESSION_KEY_CHARACTER_NAME = 'SESSION.CHARACTER.NAME';
const SESSION_KEY_CHARACTER_TIME = 'SESSION.CHARACTER.TIME';
const SESSION_KEY_CHARACTER_PREV_SYSTEM_ID = 'SESSION.CHARACTER.PREV_SYSTEM_ID';
const SESSION_KEY_CHARACTERS = 'SESSION.CHARACTERS';
//const SESSION_KEY_CHARACTERS_PREV_SYSTEM_ID = 'SESSION.CHARACTERS.PREV_SYSTEM_ID';
const SESSION_KEY_CHARACTERS_PREV_SYSTEM_ID = 'SESSION.TEST.PREV_SYSTEM_ID';
const SESSION_KEY_CHARACTER_ACCESS_TOKEN = 'SESSION.CHARACTER.ACCESS_TOKEN';
const SESSION_KEY_CHARACTER_REFRESH_TOKEN = 'SESSION.CHARACTER.REFRESH_TOKEN';
// temp login character ID (during HTTP redirects on login)
const SESSION_KEY_TEMP_CHARACTER_ID = 'SESSION.TEMP_CHARACTER_ID';
// log text
const LOG_LOGGED_IN = 'userId: [%10s], userName: [%30s], charId: [%20s], charName: %s';
@@ -45,6 +43,29 @@ class User extends Controller\Controller{
*/
private static $captchaReason = [self::SESSION_CAPTCHA_ACCOUNT_UPDATE, self::SESSION_CAPTCHA_ACCOUNT_DELETE];
/**
* merges two multidimensional characterSession arrays by checking characterID
* @param array $characterDataBase
* @param array $characterData
* @return array
*/
private function mergeSessionCharacterData($characterDataBase = [], $characterData = []){
$addData = [];
foreach($characterDataBase as $i => $baseData){
foreach($characterData as $data){
if((int)$baseData['ID'] === (int)$data['ID']){
// overwrite changes
$characterDataBase[$i]['NAME'] = $data['NAME'];
$characterDataBase[$i]['TIME'] = $data['TIME'];
}else{
$addData[] = $data;
}
}
}
return array_merge($characterDataBase, $addData);
}
/**
* login a valid character
* @param Model\CharacterModel $characterModel
@@ -54,24 +75,43 @@ class User extends Controller\Controller{
$login = false;
if($user = $characterModel->getUser()){
// set user/character data to session -------------------
$this->f3->set(self::SESSION_KEY_USER, [
'ID' => $user->_id,
'NAME' => $user->name
]);
// check if character belongs to current user
// -> If there is already a logged in user! (e.g. multi character use)
$currentUser = $this->getUser();
$dateTime = new \DateTime();
$this->f3->set(self::SESSION_KEY_CHARACTER, [
'ID' => $characterModel->_id,
'NAME' => $characterModel->name,
'TIME' => $dateTime->getTimestamp()
]);
$sessionCharacters = [
[
'ID' => $characterModel->_id,
'NAME' => $characterModel->name,
'TIME' => (new \DateTime())->getTimestamp()
]
];
// save user login information ---------------------------
if(
is_null($currentUser) ||
$currentUser->_id !== $user->_id
){
// user has changed OR new user ---------------------------------------------------
//-> set user/character data to session
$this->f3->set(self::SESSION_KEY_USER, [
'ID' => $user->_id,
'NAME' => $user->name
]);
}else{
// user has NOT changed -----------------------------------------------------------
// -> get current session characters
$currentSessionCharacters = (array)$this->f3->get(self::SESSION_KEY_CHARACTERS);
$sessionCharacters = $this->mergeSessionCharacterData($sessionCharacters, $currentSessionCharacters);
}
$this->f3->set(self::SESSION_KEY_CHARACTERS, $sessionCharacters);
// save user login information --------------------------------------------------------
$characterModel->touch('lastLogin');
$characterModel->save();
// write login log --------------------------------------
// write login log --------------------------------------------------------------------
self::getLogger('LOGIN')->write(
sprintf(self::LOG_LOGGED_IN,
$user->_id,
@@ -210,7 +250,7 @@ class User extends Controller\Controller{
if($activeCharacter = $this->getCharacter(0)){
$user = $activeCharacter->getUser();
// captcha is send -> check captcha ---------------------------------
// captcha is send -> check captcha -------------------------------------------
if(
isset($formData['captcha']) &&
!empty($formData['captcha'])
@@ -250,7 +290,7 @@ class User extends Controller\Controller{
}
}
// sharing config ---------------------------------------------------
// sharing config -------------------------------------------------------------
if(isset($formData['share'])){
$privateSharing = 0;
$corporationSharing = 0;

View File

@@ -115,6 +115,10 @@ class Sso extends Api\User{
if($loginCheck){
// set "login" cookie
$this->setLoginCookie($character);
// -> pass current character data to target page
$f3->set(Api\User::SESSION_KEY_TEMP_CHARACTER_ID, $character->_id);
// route to "map"
$f3->reroute('@map');
}
@@ -218,15 +222,18 @@ class Sso extends Api\User{
// -> update character log (current location,...)
$characterModel = $characterModel->updateLog();
// check if there is already an active user logged in
if($activeCharacter = $this->getCharacter()){
// connect character with current user
$user = $activeCharacter->getUser();
}elseif( is_null( $user = $characterModel->getUser()) ){
// no user found (new character) -> create new user and connect to character
$user = Model\BasicModel::getNew('UserModel');
$user->name = $characterModel->name;
$user->save();
// connect character with current user
if( is_null($user = $this->getUser()) ){
// connect character with existing user (no changes)
if( is_null( $user = $characterModel->getUser()) ){
// no user found (new character) -> create new user and connect to character
/**
* @var $user Model\UserModel
*/
$user = Model\BasicModel::getNew('UserModel');
$user->name = $characterModel->name;
$user->save();
}
}
/**
@@ -251,6 +258,9 @@ class Sso extends Api\User{
// set "login" cookie
$this->setLoginCookie($characterModel);
// -> pass current character data to target page
$f3->set(Api\User::SESSION_KEY_TEMP_CHARACTER_ID, $characterModel->_id);
// route to "map"
$f3->reroute('@map');
}else{
@@ -308,6 +318,10 @@ class Sso extends Api\User{
// login by character
$loginCheck = $this->loginByCharacter($character);
if($loginCheck){
// set character id
// -> pass current character data to target page
$f3->set(Api\User::SESSION_KEY_TEMP_CHARACTER_ID, $character->_id);
// route to "map"
$f3->reroute('@map');
}

View File

@@ -318,17 +318,81 @@ class Controller {
}
/**
* checks whether a user is currently logged in
* get current character data from session
* @return array
*/
protected function getSessionCharacterData(){
$data = [];
$currentSessionCharacters = (array)$this->getF3()->get(Api\User::SESSION_KEY_CHARACTERS);
$requestedCharacterId = 0;
// get all characterData from currently active characters
if($this->getF3()->get('AJAX')){
// Ajax request -> get characterId from Header (if already available!)
$header = $this->getRequestHeaders();
$requestedCharacterId = (int)$header['Pf-Character'];
if(
$requestedCharacterId > 0 &&
(int)$this->getF3()->get(Api\User::SESSION_KEY_TEMP_CHARACTER_ID) === $requestedCharacterId
){
// characterId is available in Javascript
// -> clear temp characterId for next character login/switch
$this->getF3()->clear(Api\User::SESSION_KEY_TEMP_CHARACTER_ID);
}
}
if($requestedCharacterId <= 0){
// Ajax BUT characterID not yet set as HTTP header
// OR non Ajax -> get characterId from temp session (e.g. from HTTP redirect)
$requestedCharacterId = (int)$this->getF3()->get(Api\User::SESSION_KEY_TEMP_CHARACTER_ID);
}
if($requestedCharacterId > 0){
// search for session character data
foreach($currentSessionCharacters as $characterData){
if($requestedCharacterId === (int)$characterData['ID']){
$data = $characterData;
break;
}
}
}elseif( !empty($currentSessionCharacters) ){
// no character was requested ($requestedCharacterId = 0) AND session characters were found
// -> get first matched character (e.g. user open browser tab)
$data = $currentSessionCharacters[0];
}
if( !empty($data) ){
// check if character still exists on DB (e.g. was manually removed in the meantime)
// -> This should NEVER happen just for security and "local development"
$character = Model\BasicModel::getNew('CharacterModel');
$character->getById( (int)$data['ID']);
if(
$character->dry() ||
!$character->hasUserCharacter()
){
// character data is invalid!
$data = [];
}
}
return $data;
}
/**
* checks whether a user/character is currently logged in
* @param \Base $f3
* @return bool
*/
protected function checkLogTimer($f3){
$loginCheck = false;
$characterData = $this->getSessionCharacterData();
if($f3->get(Api\User::SESSION_KEY_CHARACTER_TIME) > 0){
if( !empty($characterData) ){
// check logIn time
$logInTime = new \DateTime();
$logInTime->setTimestamp( $f3->get(Api\User::SESSION_KEY_CHARACTER_TIME) );
$logInTime->setTimestamp( (int)$characterData['TIME'] );
$now = new \DateTime();
$timeDiff = $now->diff($logInTime);
@@ -346,35 +410,66 @@ class Controller {
}
/**
* get current character model
* get current character
* @param int $ttl
* @return Model\CharacterModel|null
* @throws \Exception
*/
public function getCharacter($ttl = 0){
$character = null;
$characterData = $this->getSessionCharacterData();
if( $this->getF3()->exists(Api\User::SESSION_KEY_CHARACTER_ID) ){
$characterId = (int)$this->getF3()->get(Api\User::SESSION_KEY_CHARACTER_ID);
if($characterId){
/**
* @var $characterModel Model\CharacterModel
*/
$characterModel = Model\BasicModel::getNew('CharacterModel');
$characterModel->getById($characterId, $ttl);
if( !empty($characterData) ){
/**
* @var $characterModel Model\CharacterModel
*/
$characterModel = Model\BasicModel::getNew('CharacterModel');
$characterModel->getById( (int)$characterData['ID'], $ttl);
if(
!$characterModel->dry() &&
$characterModel->hasUserCharacter()
){
$character = &$characterModel;
}
if(
!$characterModel->dry() &&
$characterModel->hasUserCharacter()
){
$character = &$characterModel;
}
}
return $character;
}
/**
* get current user
* @param int $ttl
* @return Model\UserModel|null
*/
public function getUser($ttl = 0){
$user = null;
if( $this->getF3()->exists(Api\User::SESSION_KEY_USER_ID) ){
$userId = (int)$this->getF3()->get(Api\User::SESSION_KEY_USER_ID);
if($userId){
/**
* @var $userModel Model\UserModel
*/
$userModel = Model\BasicModel::getNew('UserModel');
$userModel->getById($userId, $ttl);
if(
!$userModel->dry() &&
$userModel->hasUserCharacters()
){
$user = &$userModel;
}
}
}
return $user;
}
public function getCharacterSessionData(){
}
/**
* log out current character
* @param \Base $f3
@@ -384,7 +479,7 @@ class Controller {
// ----------------------------------------------------------
// delete server side cookie validation data
// for the current character as well
// for the active character
if(
$params['clearCookies'] === '1' &&
( $activeCharacter = $this->getCharacter())

View File

@@ -212,12 +212,12 @@ class CharacterLogModel extends BasicModel {
( $activeCharacter = $controller->getCharacter() ) &&
( $activeCharacter->_id === $this->characterId->_id )
){
$prevSystemId = (int)$f3->get( User::SESSION_KEY_CHARACTER_PREV_SYSTEM_ID);
$prevSystemId = (int)$f3->get( User::SESSION_KEY_CHARACTERS_PREV_SYSTEM_ID);
if($prevSystemId === 0){
$f3->set( User::SESSION_KEY_CHARACTER_PREV_SYSTEM_ID, $systemId);
$f3->set( User::SESSION_KEY_CHARACTERS_PREV_SYSTEM_ID, $systemId);
}else{
$f3->set( User::SESSION_KEY_CHARACTER_PREV_SYSTEM_ID, (int)$this->systemId);
$f3->set( User::SESSION_KEY_CHARACTERS_PREV_SYSTEM_ID, (int)$this->systemId);
}
}
}

View File

@@ -133,6 +133,16 @@ class UserModel extends BasicModel {
}
}
/**
* check whether this character has already a user assigned to it
* @return bool
*/
public function hasUserCharacters(){
$this->filter('userCharacters', ['active = ?', 1]);
return is_object($this->userCharacters);
}
/**
* search for user by unique username
* @param $name

View File

@@ -508,7 +508,7 @@ define([
var getLocaleData = function(type, objectId){
if(objectId > 0){
var storageKey = getLocalStoragePrefixByType(type) + objectId;
return Util.localforage.getItem(storageKey);
return Util.getLocalStorage().getItem(storageKey);
}else{
console.warn('Local storage requires object id > 0');
}
@@ -525,13 +525,13 @@ define([
if(objectId > 0){
// get current map config
var storageKey = getLocalStoragePrefixByType(type) + objectId;
Util.localforage.getItem(storageKey).then(function(data) {
Util.getLocalStorage().getItem(storageKey).then(function(data) {
// This code runs once the value has been loaded
// from the offline store.
data = (data === null) ? {} : data;
// set/update value
data[this.key] = this.value;
Util.localforage.setItem(this.storageKey, data);
Util.getLocalStorage().setItem(this.storageKey, data);
}.bind({
key: key,
value: value,
@@ -555,13 +555,13 @@ define([
if(objectId > 0){
// get current map config
var storageKey = getLocalStoragePrefixByType(type) + objectId;
Util.localforage.getItem(storageKey).then(function(data) {
Util.getLocalStorage().getItem(storageKey).then(function(data) {
if(
data &&
data.hasOwnProperty(key)
){
delete data[key];
Util.localforage.setItem(this.storageKey, data);
Util.getLocalStorage().setItem(this.storageKey, data);
}
}.bind({
storageKey: storageKey

View File

@@ -19,7 +19,10 @@ define([
* main init "map" page
*/
$(function(){
// set Dialog default config
// set default AJAX config
Util.ajaxSetup();
// set default dialog config
Util.initDefaultBootboxConfig();
// load page
@@ -28,11 +31,6 @@ define([
// show app information in browser console
Util.showVersionInfo();
// init local storage
Util.localforage.config({
name: 'Pathfinder local storage'
});
// init logging
Logging.init();

View File

@@ -573,10 +573,11 @@ define([
$(document).on('pf:menuLogout', function(e, data){
var clearCookies = false;
if( typeof data === 'object' ){
if( data.hasOwnProperty('clearCookies') ){
clearCookies = data.clearCookies;
}
if(
typeof data === 'object' &&
data.hasOwnProperty('clearCookies')
){
clearCookies = data.clearCookies;
}
// logout
@@ -674,6 +675,7 @@ define([
var userInfoElement = $('.' + config.headUserCharacterClass);
var currentCharacterId = userInfoElement.data('characterId');
var currentCharactersOptionIds = userInfoElement.data('characterOptionIds') ? userInfoElement.data('characterOptionIds') : [];
var newCharacterId = 0;
var newCharacterName = '';
@@ -691,7 +693,6 @@ define([
visibility : 'hidden',
duration: 500,
complete: function(){
// callback
callback();
@@ -727,25 +728,31 @@ define([
}
}
// update user character data ---------------------------------------------------
if(currentCharacterId !== newCharacterId){
var newCharactersOptionIds = userData.characters.map(function(data){
return data.id;
});
var showCharacterElement = true;
if(newCharacterId === 0){
showCharacterElement = false;
// update user character data ---------------------------------------------------
if(currentCharactersOptionIds.toString() !== newCharactersOptionIds.toString()){
var currentCharacterChanged = false;
if(currentCharacterId !== newCharacterId){
currentCharacterChanged = true;
}
// toggle element
animateHeaderElement(userInfoElement, function(){
userInfoElement.find('span').text( newCharacterName );
userInfoElement.find('img').attr('src', Init.url.ccpImageServer + 'Character/' + newCharacterId + '_32.jpg' );
if(currentCharacterChanged){
userInfoElement.find('span').text( newCharacterName );
userInfoElement.find('img').attr('src', Init.url.ccpImageServer + 'Character/' + newCharacterId + '_32.jpg' );
}
// init "character switch" popover
userInfoElement.initCharacterSwitchPopover(userData);
}, showCharacterElement);
}, true);
// set new id for next check
// store new id(s) for next check
userInfoElement.data('characterId', newCharacterId);
userInfoElement.data('characterOptionIds', newCharactersOptionIds);
}
// update user ship data --------------------------------------------------------

View File

@@ -64,6 +64,8 @@ define([
var animationTimerCache = {}; // cache for table row animation timeout
var localStorage; // cache for "localForage" singleton
/*
* ===========================================================================================================
* Global jQuery plugins for some common and frequently used functions
@@ -589,40 +591,48 @@ define([
return elements.each(function() {
var element = $(this);
// destroy "popover" and remove "click" event for animation
element.popover('destroy').off();
// init popover and add specific class to it (for styling)
element.popover({
html: true,
title: 'select character',
trigger: 'click',
placement: 'bottom',
container: 'body',
content: content,
animation: false
}).data('bs.popover').tip().addClass('pf-popover');
// check if tooltip already exists -> remove it
if(element.data('bs.popover') !== undefined){
element.off('click').popover('destroy');
}
element.on('click', function(e) {
e.preventDefault();
e.stopPropagation();
var easeEffect = $(this).attr('data-easein');
var popover = $(this).data('bs.popover').tip();
var popoverData = $(this).data('bs.popover');
var popoverElement = null;
var velocityOptions = {
duration: Init.animationSpeed.dialogEvents
};
switch(easeEffect){
case 'shake':
case 'pulse':
case 'tada':
case 'flash':
case 'bounce':
case 'swing':
popover.velocity('callout.' + easeEffect, velocityOptions);
break;
default:
popover.velocity('transition.' + easeEffect, velocityOptions);
break;
if(popoverData === undefined){
// init popover and add specific class to it (for styling)
$(this).popover({
html: true,
title: 'select character',
trigger: 'manual',
placement: 'bottom',
container: 'body',
content: content,
animation: false
}).data('bs.popover').tip().addClass('pf-popover');
$(this).popover('show');
popoverElement = $(this).data('bs.popover').tip();
popoverElement.velocity('transition.' + easeEffect, velocityOptions);
}else{
popoverElement = $(this).data('bs.popover').tip();
if(popoverElement.is(':visible')){
popoverElement.velocity('reverse');
}else{
$(this).popover('show');
popoverElement.velocity('transition.' + easeEffect, velocityOptions);
}
}
});
@@ -650,7 +660,14 @@ define([
popoverElement.has(e.target).length === 0 &&
$('.popover').has(e.target).length === 0
){
popoverElement.popover('hide');
var popover = popoverElement.data('bs.popover');
if(
popover !== undefined &&
popover.tip().is(':visible')
){
popoverElement.popover('hide');
}
}
});
});
@@ -1014,6 +1031,28 @@ define([
return logInfo;
};
/**
* set default jQuery AJAX configuration
*/
var ajaxSetup = function(){
$.ajaxSetup({
beforeSend: function(xhr) {
// add current character data to ANY XHR request (HTTP HEADER)
// -> This helps to identify multiple characters on multiple browser tabs
var userData = getCurrentUserData();
var currentCharacterId = 0;
if(
userData &&
userData.character
){
currentCharacterId = parseInt( userData.character.id );
}
xhr.setRequestHeader('Pf-Character', currentCharacterId);
}
});
};
/**
* Returns true if the user hit Esc or navigated away from the
* current page before an AJAX call was done. (The response
@@ -1665,6 +1704,20 @@ define([
return price + ' ISK';
};
/**
* get localForage instance (singleton) for offline client site storage
* @returns {localforage}
*/
var getLocalStorage = function(){
if(localStorage === undefined){
localStorage = localforage.createInstance({
driver: localforage.INDEXEDDB,
name: 'Pathfinder local storage'
});
}
return localStorage;
};
/**
* Create Date as UTC
* @param date
@@ -1756,7 +1809,6 @@ define([
return {
config: config,
localforage: localforage,
showVersionInfo: showVersionInfo,
initDefaultBootboxConfig: initDefaultBootboxConfig,
getCurrentTriggerDelay: getCurrentTriggerDelay,
@@ -1769,6 +1821,7 @@ define([
showNotify: showNotify,
stopTabBlink: stopTabBlink,
getLogInfo: getLogInfo,
ajaxSetup: ajaxSetup,
isXHRAborted: isXHRAborted,
getMapModule: getMapModule,
getSystemEffectData: getSystemEffectData,
@@ -1797,6 +1850,7 @@ define([
convertDateToString: convertDateToString,
getOpenDialogs: getOpenDialogs,
formatPrice: formatPrice,
getLocalStorage: getLocalStorage,
getDocumentPath: getDocumentPath,
redirect: redirect,
logout: logout

View File

@@ -508,7 +508,7 @@ define([
var getLocaleData = function(type, objectId){
if(objectId > 0){
var storageKey = getLocalStoragePrefixByType(type) + objectId;
return Util.localforage.getItem(storageKey);
return Util.getLocalStorage().getItem(storageKey);
}else{
console.warn('Local storage requires object id > 0');
}
@@ -525,13 +525,13 @@ define([
if(objectId > 0){
// get current map config
var storageKey = getLocalStoragePrefixByType(type) + objectId;
Util.localforage.getItem(storageKey).then(function(data) {
Util.getLocalStorage().getItem(storageKey).then(function(data) {
// This code runs once the value has been loaded
// from the offline store.
data = (data === null) ? {} : data;
// set/update value
data[this.key] = this.value;
Util.localforage.setItem(this.storageKey, data);
Util.getLocalStorage().setItem(this.storageKey, data);
}.bind({
key: key,
value: value,
@@ -555,13 +555,13 @@ define([
if(objectId > 0){
// get current map config
var storageKey = getLocalStoragePrefixByType(type) + objectId;
Util.localforage.getItem(storageKey).then(function(data) {
Util.getLocalStorage().getItem(storageKey).then(function(data) {
if(
data &&
data.hasOwnProperty(key)
){
delete data[key];
Util.localforage.setItem(this.storageKey, data);
Util.getLocalStorage().setItem(this.storageKey, data);
}
}.bind({
storageKey: storageKey

View File

@@ -19,7 +19,10 @@ define([
* main init "map" page
*/
$(function(){
// set Dialog default config
// set default AJAX config
Util.ajaxSetup();
// set default dialog config
Util.initDefaultBootboxConfig();
// load page
@@ -28,11 +31,6 @@ define([
// show app information in browser console
Util.showVersionInfo();
// init local storage
Util.localforage.config({
name: 'Pathfinder local storage'
});
// init logging
Logging.init();

View File

@@ -573,10 +573,11 @@ define([
$(document).on('pf:menuLogout', function(e, data){
var clearCookies = false;
if( typeof data === 'object' ){
if( data.hasOwnProperty('clearCookies') ){
clearCookies = data.clearCookies;
}
if(
typeof data === 'object' &&
data.hasOwnProperty('clearCookies')
){
clearCookies = data.clearCookies;
}
// logout
@@ -674,6 +675,7 @@ define([
var userInfoElement = $('.' + config.headUserCharacterClass);
var currentCharacterId = userInfoElement.data('characterId');
var currentCharactersOptionIds = userInfoElement.data('characterOptionIds') ? userInfoElement.data('characterOptionIds') : [];
var newCharacterId = 0;
var newCharacterName = '';
@@ -691,7 +693,6 @@ define([
visibility : 'hidden',
duration: 500,
complete: function(){
// callback
callback();
@@ -727,25 +728,31 @@ define([
}
}
// update user character data ---------------------------------------------------
if(currentCharacterId !== newCharacterId){
var newCharactersOptionIds = userData.characters.map(function(data){
return data.id;
});
var showCharacterElement = true;
if(newCharacterId === 0){
showCharacterElement = false;
// update user character data ---------------------------------------------------
if(currentCharactersOptionIds.toString() !== newCharactersOptionIds.toString()){
var currentCharacterChanged = false;
if(currentCharacterId !== newCharacterId){
currentCharacterChanged = true;
}
// toggle element
animateHeaderElement(userInfoElement, function(){
userInfoElement.find('span').text( newCharacterName );
userInfoElement.find('img').attr('src', Init.url.ccpImageServer + 'Character/' + newCharacterId + '_32.jpg' );
if(currentCharacterChanged){
userInfoElement.find('span').text( newCharacterName );
userInfoElement.find('img').attr('src', Init.url.ccpImageServer + 'Character/' + newCharacterId + '_32.jpg' );
}
// init "character switch" popover
userInfoElement.initCharacterSwitchPopover(userData);
}, showCharacterElement);
}, true);
// set new id for next check
// store new id(s) for next check
userInfoElement.data('characterId', newCharacterId);
userInfoElement.data('characterOptionIds', newCharactersOptionIds);
}
// update user ship data --------------------------------------------------------

View File

@@ -64,6 +64,8 @@ define([
var animationTimerCache = {}; // cache for table row animation timeout
var localStorage; // cache for "localForage" singleton
/*
* ===========================================================================================================
* Global jQuery plugins for some common and frequently used functions
@@ -589,40 +591,48 @@ define([
return elements.each(function() {
var element = $(this);
// destroy "popover" and remove "click" event for animation
element.popover('destroy').off();
// init popover and add specific class to it (for styling)
element.popover({
html: true,
title: 'select character',
trigger: 'click',
placement: 'bottom',
container: 'body',
content: content,
animation: false
}).data('bs.popover').tip().addClass('pf-popover');
// check if tooltip already exists -> remove it
if(element.data('bs.popover') !== undefined){
element.off('click').popover('destroy');
}
element.on('click', function(e) {
e.preventDefault();
e.stopPropagation();
var easeEffect = $(this).attr('data-easein');
var popover = $(this).data('bs.popover').tip();
var popoverData = $(this).data('bs.popover');
var popoverElement = null;
var velocityOptions = {
duration: Init.animationSpeed.dialogEvents
};
switch(easeEffect){
case 'shake':
case 'pulse':
case 'tada':
case 'flash':
case 'bounce':
case 'swing':
popover.velocity('callout.' + easeEffect, velocityOptions);
break;
default:
popover.velocity('transition.' + easeEffect, velocityOptions);
break;
if(popoverData === undefined){
// init popover and add specific class to it (for styling)
$(this).popover({
html: true,
title: 'select character',
trigger: 'manual',
placement: 'bottom',
container: 'body',
content: content,
animation: false
}).data('bs.popover').tip().addClass('pf-popover');
$(this).popover('show');
popoverElement = $(this).data('bs.popover').tip();
popoverElement.velocity('transition.' + easeEffect, velocityOptions);
}else{
popoverElement = $(this).data('bs.popover').tip();
if(popoverElement.is(':visible')){
popoverElement.velocity('reverse');
}else{
$(this).popover('show');
popoverElement.velocity('transition.' + easeEffect, velocityOptions);
}
}
});
@@ -650,7 +660,14 @@ define([
popoverElement.has(e.target).length === 0 &&
$('.popover').has(e.target).length === 0
){
popoverElement.popover('hide');
var popover = popoverElement.data('bs.popover');
if(
popover !== undefined &&
popover.tip().is(':visible')
){
popoverElement.popover('hide');
}
}
});
});
@@ -1014,6 +1031,28 @@ define([
return logInfo;
};
/**
* set default jQuery AJAX configuration
*/
var ajaxSetup = function(){
$.ajaxSetup({
beforeSend: function(xhr) {
// add current character data to ANY XHR request (HTTP HEADER)
// -> This helps to identify multiple characters on multiple browser tabs
var userData = getCurrentUserData();
var currentCharacterId = 0;
if(
userData &&
userData.character
){
currentCharacterId = parseInt( userData.character.id );
}
xhr.setRequestHeader('Pf-Character', currentCharacterId);
}
});
};
/**
* Returns true if the user hit Esc or navigated away from the
* current page before an AJAX call was done. (The response
@@ -1665,6 +1704,20 @@ define([
return price + ' ISK';
};
/**
* get localForage instance (singleton) for offline client site storage
* @returns {localforage}
*/
var getLocalStorage = function(){
if(localStorage === undefined){
localStorage = localforage.createInstance({
driver: localforage.INDEXEDDB,
name: 'Pathfinder local storage'
});
}
return localStorage;
};
/**
* Create Date as UTC
* @param date
@@ -1756,7 +1809,6 @@ define([
return {
config: config,
localforage: localforage,
showVersionInfo: showVersionInfo,
initDefaultBootboxConfig: initDefaultBootboxConfig,
getCurrentTriggerDelay: getCurrentTriggerDelay,
@@ -1769,6 +1821,7 @@ define([
showNotify: showNotify,
stopTabBlink: stopTabBlink,
getLogInfo: getLogInfo,
ajaxSetup: ajaxSetup,
isXHRAborted: isXHRAborted,
getMapModule: getMapModule,
getSystemEffectData: getSystemEffectData,
@@ -1797,6 +1850,7 @@ define([
convertDateToString: convertDateToString,
getOpenDialogs: getOpenDialogs,
formatPrice: formatPrice,
getLocalStorage: getLocalStorage,
getDocumentPath: getDocumentPath,
redirect: redirect,
logout: logout