Skip to content

Instantly share code, notes, and snippets.

@AnatoliyLitinskiy
Created October 7, 2015 12:19
Show Gist options
  • Save AnatoliyLitinskiy/2c3e175099ef5219ed1f to your computer and use it in GitHub Desktop.
Save AnatoliyLitinskiy/2c3e175099ef5219ed1f to your computer and use it in GitHub Desktop.
Don't allow open several tabs
angular.module('myMod').factory('tabsData', function (localStorageService) {
var Factory = {},
keyReg = /^tab-(\d+-\d+)$/;
/**
* get all tabs data
* @return array
**/
Factory.all = function () {
var keys = localStorageService.keys(),
ret = [];
keys.map(function(key) {
if (key.match(keyReg)) {
ret.push(localStorageService.get(key));
}
});
return ret;
};
/**
* remove tab data by id
**/
Factory.remove = function (id) {
localStorageService.remove('tab-' + id);
};
/**
* add tab data with id, and update / creation time
**/
Factory.add = function (id) {
var t = (new Date()).toString(),
tabData = {id:id, t: t};
localStorageService.set('tab-' + id, tabData);
};
/**
* get tab data or null
**/
Factory.get = function (id) {
var tabData = localStorageService.get('tab-' + id);
return tabData;
};
/**
* udpate tab data.t and remove data.markedToRemove
* if it set
**/
Factory.update = function (id) {
var tabData = Factory.get(id);
if (tabData === undefined || tabData === null) {
console.error("invalid tabData id", id);
return;
}
var t = (new Date()).toString();
tabData.t = t;
tabData.markedToRemove = undefined;
localStorageService.set('tab-' + id, tabData);
};
/**
* add tab data.markedToRemove with current Date toString()
* as data.markedToRemove param
**/
Factory.markToRemove = function (id) {
var tabData = Factory.get(id);
if (tabData === undefined || tabData === null) {
console.error("invalid tabData id", id);
return;
}
var t = (new Date()).toString();
tabData.markedToRemove = t;
localStorageService.set('tab-' + id, tabData);
};
/**
* unique id generation
**/
function getRandomArbitary (min, max) {
return Math.floor(Math.random() * (max - min) + min);
}
Factory.generateId = function () {
return getRandomArbitary(0,9999) + '-' + (new Date()).getTime();
}
return Factory;
});
angular.module('myMod').factory('tabsTracker', function (tabsData) {
var Factory = {ids: []},
checkingPeriod = 800,
removeAfterMarkingTime = checkingPeriod * 3,
tabNotAliveAfter = 5000;
/**
* do not allow open several tabs on certain pages
* (it works with pages, generated on fly with ng-location,
* please use it only with topmost $scope / template in nested templates / locations)
**/
Factory.track = function ($scope) {
/**
* do not allow track single tab in same scope
**/
if ($scope.trackingTabId) {
throw new Error("tab already tracked with current scope");
return null;
}
/**
* do not allow track single tab in same app
**/
if (Factory.ids.length) {
throw new Error("tab already tracked with another scope");
return null;
}
var id = tabsData.generateId();
tabsData.add(id);
$scope.trackingTabId = id;
/**
* save these id to Factory to not allow track tab twice in
* same ng-app
**/
Factory.ids.push(id);
/**
* right away after tracking this tab we can block
* it if other tab was opened
**/
_checkOpenedTabsData($scope);
var interval = setInterval(_checkOpenedTabsData, checkingPeriod, $scope);
var destroyIdOnPageClose = function (/*event*/) {
clearInterval(interval);
_destroyId(id);
}
/**
* destroy tabData when pages will be closed/redirected
**/
window.addEventListener('beforeunload', destroyIdOnPageClose);
/**
* destroy tabData on $scope $destroy event
* note that $destroy event doesnt fires when we close tab,
* it fires only in normal angular-js workflow
**/
$scope.$on('$destroy', function ($event) {
window.removeEventListener('beforeunload', destroyIdOnPageClose);
_destroyId(id);
clearInterval(interval);
});
}
/**
* set $scope.tooManyTabs true / false
**/
function _checkOpenedTabsData ($scope) {
var tooManyTabsOldVal = $scope.tooManyTabs;
if (!_checkIsTabSingleAndRmoveOldTabsData($scope.trackingTabId)) {
$scope.tooManyTabs = true;
} else {
$scope.tooManyTabs = false;
}
if (tooManyTabsOldVal != $scope.tooManyTabs && $scope.$$phase === null) {
$scope.$apply();
}
}
/**
* check if current tab is single and remove not alive tabs data
**/
function _checkIsTabSingleAndRmoveOldTabsData (tabTrackingId) {
var _tabs_data_arr = tabsData.all(),
currentDateTime = new Date(),
/**
* @var bool currentTabSingle
**/
currentTabSingle = true;
/**
* check that _tabs_data_arr.length contains only current tab data
*/
if (_tabs_data_arr.length === 1 && _tabs_data_arr[0].id == tabTrackingId ) {
return currentTabSingle;
} else {
tabsData.update(tabTrackingId);
}
for (var i = 0; i < _tabs_data_arr.length; i++) {
var data = _tabs_data_arr[i];
/**
* skip current tab
**/
if (data.id == tabTrackingId) {
continue;
}
var tabNotAlive = (currentDateTime - new Date(data.t)) > tabNotAliveAfter;
/**
* mark to remove and wait if tab is live it will update his data.t
* or remove not live tabs data that was already marked
**/
if (tabNotAlive) {
_removeMarkedOldTabData(data);
_markToRemove(data);
continue;
}
currentTabSingle = false;
}
return currentTabSingle;
}
function _destroyId (tabTrackingId) {
Factory.ids.removeItem(tabTrackingId);
tabsData.remove(tabTrackingId);
}
function _markToRemove (tabData) {
var isMarked = tabData.markedToRemove !== undefined;
if (!isMarked) {
tabsData.markToRemove(tabData.id);
}
}
function _removeMarkedOldTabData (tabData) {
var isMarked = tabData.markedToRemove !== undefined;
if (!isMarked) {
return;
}
var currentDateTime = new Date(),
markedDateTime = new Date(tabData.markedToRemove),
readyToRemove = currentDateTime - markedDateTime > removeAfterMarkingTime;
if (readyToRemove) {
tabsData.remove(tabData.id);
}
}
return Factory;
});

do not allow open servarl tabs

angular.module('myMod').controller('pageCtrl', function ($scope, tabsTracker) {
  tabsTracker.track($scope);
});
<div class="overlay" ng-show="tooManyTabs">
  <div class="popup">
    Please close this tab and resume the other nomination.
  </div>
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment