%]
[% BLOCK APP_JS %]
+<div id='foo'></div>
<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/grid.js"></script>
<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/ui.js"></script>
<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/user.js"></script>
<script src="[% ctx.media_prefix %]/js/ui/default/staff/admin/workstation/app.js"></script>
<script>
-angular.module('egCoreMod')
-.factory('egAppStrings', function() {return {
-PREFS_REMOVE_KEY_CONFIRM :
- '[% l('Delete content for key "[_1]"?', '{{deleteKey}}') %]',
-DEFAULT_WS_LABEL : '[% l('[_1] (Default)', '{{ws}}') %]'
-}});
+angular.module('egCoreMod').run(['egStrings', function(s) {
+ s.PREFS_REMOVE_KEY_CONFIRM =
+ '[% l('Delete content for key "[_1]"?', '{{deleteKey}}') %]';
+ s.DEFAULT_WS_LABEL = '[% l('[_1] (Default)', '{{ws}}') %]';
+}]);
</script>
[% END %]
<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/user.js"></script>
<script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/checkin/app.js"></script>
<script>
-// this could live in a .tt2 file instead -- if it gets large
-angular.module('egCoreMod').factory('egCheckinStrings', function() {return {
-UNCAT_ALERT_DIALOG :
- '[% l('Copy "{{copy_barcode}}" was mis-scanned or is not cataloged') %]',
-COPY_ALERT_MSG_DIALOG_TITLE :
- '[% l('Copy Alert Message for {{copy_barcode}}') %]'
-}});
+angular.module('egCoreMod').run(['egStrings', function(s) {
+s.UNCAT_ALERT_DIALOG =
+ '[% l('Copy "{{copy_barcode}}" was mis-scanned or is not cataloged') %]';
+s.COPY_ALERT_MSG_DIALOG_TITLE =
+ '[% l('Copy Alert Message for {{copy_barcode}}') %]';
+}]);
</script>
[% END %]
<link rel="stylesheet" href="[% ctx.base_path %]/staff/css/circ/patron.css" />
<script>
-// this could live in a .tt2 file instead -- if it gets large
-angular.module('egCoreMod').factory('egAppStrings', function() {return {
-OPT_IN_DIALOG :
- '[% l('Does patron {{user.family_name()}}, {{user.first_given_name()}} from {{org.name()}} ({{org.shortname()}}) consent to having their personal information shared with your library?') %]'
-}});
+angular.module('egCoreMod').run(['egStrings', function(s) {
+ s.OPT_IN_DIALOG =
+"[% l("Does patron {{user.family_name()}}, {{user.first_given_name()}} from {{org.name()}} ({{org.shortname()}}) consent to having their personal information shared with your library?") %]"
+}]);
</script>
[% END %]
<!-- angular-driven shared services -->
<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/core.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/strings.js"></script>
<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/idl.js"></script>
<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/event.js"></script>
<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/net.js"></script>
<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/env.js"></script>
<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/org.js"></script>
<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/startup.js"></script>
-<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/printstore.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/hatch.js"></script>
<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/coresvc.js"></script>
<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/ui.js"></script>
}])
.controller('SplashCtrl',
- ['$scope','$window','$location','$interpolate','egPrintStore','egAuth','egOrg','egPerm','egEvent','egNet','egAppStrings',
-function($scope , $window , $location , $interpolate , egPrintStore , egAuth , egOrg , egPerm , egEvent , egNet , egAppStrings) {
+ ['$scope','$window','$location','egCore',
+function($scope , $window , $location , egCore) {
var allWorkstations = [];
var permMap = {};
- $scope.contextOrg = egOrg.get(egAuth.user().ws_ou());
+ $scope.contextOrg = egCore.org.get(egCore.auth.user().ws_ou());
- egPerm.hasPermAt('REGISTER_WORKSTATION', true)
+ egCore.perm.hasPermAt('REGISTER_WORKSTATION', true)
.then(function(orgList) {
// hide orgs in the context org selector where this login
});
// fetch the stored WS info
- egPrintStore.getItem('eg.conf.workstation.all')
+ egCore.hatch.getItem('eg.conf.workstation.all')
.then(function(all) {
allWorkstations = all || [];
$scope.workstations =
allWorkstations.map(function(w) { return w.name });
- return egPrintStore.getItem('eg.conf.workstation.default');
+ return egCore.hatch.getItem('eg.conf.workstation.default');
})
.then(function(def) {
$scope.defaultWS = def;
- $scope.activeWS = $scope.selectedWS = egAuth.workstation() || def;
+ $scope.activeWS = $scope.selectedWS = egCore.auth.workstation() || def;
});
$scope.getWSLabel = function(ws) {
return ws == $scope.defaultWS ?
- $interpolate(egAppStrings.DEFAULT_WS_LABEL)({ws:ws}) : ws;
+ egCore.strings.$replace(egCore.strings.DEFAULT_WS_LABEL, {ws:ws}) : ws;
}
$scope.setDefaultWS = function() {
- egPrintStore.setItem(
+ egCore.hatch.setItem(
'eg.conf.workstation.default', $scope.selectedWS)
.then(function() { $scope.defaultWS = $scope.selectedWS });
}
$scope.registerWS = function() {
var newName = $scope.contextOrg.shortname() + '-' + $scope.newWSName;
- egNet.request(
+ egCore.net.request(
'open-ils.actor',
'open-ils.actor.workstation.register',
- egAuth.token(), newName, $scope.contextOrg.id())
+ egCore.auth.token(), newName, $scope.contextOrg.id())
.then(function(resp) {
- var evt = egEvent.parse(resp);
+ var evt = egCore.evt.parse(resp);
if (evt) {
if (evt.textcode == 'WORKSTATION_NAME_EXISTS') {
console.log(evt);
owning_lib : $scope.contextOrg.id()
});
- egPrintStore.setItem(
+ egCore.hatch.setItem(
'eg.conf.workstation.all', allWorkstations)
.then(function() {
if (allWorkstations.length == 1) {
$scope.contextOrg = org;
}
- $scope.updateUsesWorkstation = function() {
- egPrintStore.setLocalItem(
- 'eg.conf.workstation.required', $scope.usesWorkstation);
- }
-
// ---------------------
// Hatch Configs
- $scope.hatchURL = egPrintStore.hatchURL();
+ $scope.hatchURL = egCore.hatch.hatchURL();
$scope.hatchRequired =
- egPrintStore.getLocalItem('eg.conf.hatch.required');
+ egCore.hatch.getLocalItem('eg.conf.hatch.required');
$scope.updateHatchRequired = function() {
- egPrintStore.setLocalItem(
+ egCore.hatch.setLocalItem(
'eg.conf.hatch.required', $scope.hatchRequired);
}
$scope.updateHatchURL = function() {
- egPrintStore.setLocalItem(
+ egCore.hatch.setLocalItem(
'eg.conf.hatch.url', $scope.hatchURL);
}
}])
.controller('PrintingCtrl',
- ['$scope','egPrintStore',
-function($scope , egPrintStore) {
+ ['$scope','egCore',
+function($scope , egCore) {
console.log('PrintingCtrl');
$scope.actionPending = false;
}
// fetch info on all remote printers
- egPrintStore.getPrinters()
+ egCore.hatch.getPrinters()
.then(function(printers) {
$scope.printers = printers;
$scope.defaultPrinter =
$scope.getPrinterByAttr('is-default', true);
})
- .then(function() { return egPrintStore.getPrintConfig() })
+ .then(function() { return egCore.hatch.getPrintConfig() })
.then(function(config) {
$scope.printConfig = config;
$scope.defaultPrinter.name;
}
- egPrintStore.setPrintConfig($scope.printConfig)
+ egCore.hatch.setPrintConfig($scope.printConfig)
.finally(function() {$scope.actionPending = false});
}
$scope.configurePrinter = function() {
$scope.printConfigError = null;
$scope.actionPending = true;
- egPrintStore.configurePrinter(
+ egCore.hatch.configurePrinter(
$scope.context,
$scope.printConfig[$scope.context].printer
)
$scope.testPrint = function(withDialog) {
if ($scope.contentType == 'text/plain') {
- egPrintStore.print(
+ egCore.hatch.print(
$scope.context,
$scope.contentType,
$scope.textPrintContent,
withDialog
);
} else {
- egPrintStore.print(
+ egCore.hatch.print(
$scope.context,
$scope.contentType,
$scope.htmlPrintContent,
}])
.controller('StoredPrefsCtrl',
- ['$scope','$q','egAuth','egPrintStore','egConfirmDialog','egAppStrings','egPerm',
-function($scope , $q , egAuth , egPrintStore , egConfirmDialog , egAppStrings , egPerm) {
+ ['$scope','$q','egCore','egConfirmDialog',
+function($scope , $q , egCore , egConfirmDialog) {
console.log('StoredPrefsCtrl');
$scope.setContext = function(ctx) {
// grab the edit perm
$scope.userHasDeletePerm = false;
- egPerm.hasPermHere('ADMIN_WORKSTATION')
+ egCore.perm.hasPermHere('ADMIN_WORKSTATION')
.then(function(bool) { $scope.userHasDeletePerm = bool });
// fetch the keys
function refreshKeys() {
$scope.keys = {local : [], remote : []};
- egPrintStore.getRemoteKeys().then(
+ egCore.hatch.getRemoteKeys().then(
function(keys) { $scope.keys.remote = keys.sort() })
// local calls are non-async
- $scope.keys.local = egPrintStore.getLocalKeys();
+ $scope.keys.local = egCore.hatch.getLocalKeys();
}
refreshKeys();
$scope.currentKeyContent = null;
if ($scope.context == 'local') {
- $scope.currentKeyContent = egPrintStore.getLocalItem(key);
+ $scope.currentKeyContent = egCore.hatch.getLocalItem(key);
} else {
- egPrintStore.getRemoteItem(key)
+ egCore.hatch.getRemoteItem(key)
.then(function(content) {
$scope.currentKeyContent = content
});
$scope.removeKey = function(key) {
egConfirmDialog.open(
- egAppStrings.PREFS_REMOVE_KEY_CONFIRM, '',
+ egCore.strings.PREFS_REMOVE_KEY_CONFIRM, '',
{ deleteKey : key,
ok : function() {
if ($scope.context == 'local') {
- egPrintStore.removeLocalItem(key);
+ egCore.hatch.removeLocalItem(key);
refreshKeys();
} else {
- egPrintStore.removeItem(key)
+ egCore.hatch.removeItem(key)
.then(function() { refreshKeys() });
}
},
* Route resolvers allow us to run async commands
* before the page controller is instantiated.
*/
- var resolver = {delay : function(egStartup) {return egStartup.go()}};
+ var resolver = {delay : ['egCore',
+ function(egCore) {return egCore.startup.go()}]};
$routeProvider.when('/login', {
templateUrl: './t_login',
controller: 'LoginCtrl',
- resolve : {delay : function(egStartup, egAuth) {
+ resolve : {delay : ['egCore', function(egCore) {
// hack for now to kill the base ses cookie where sub-path
// apps were unable to remove it. See note at the top of
// services/auth.js about angular cookies and paths.
- egAuth.logout();
- return egStartup.go();
- }}
+ egCore.auth.logout();
+ return egCore.startup.go();
+ }]}
});
// default page
/* inject services into our controller. Spelling them
* out like this allows the auto-magic injector to work
* even if the code has been minified */
- ['$scope','$location','$window','egAuth','egPrintStore',
- function($scope , $location , $window , egAuth , egPrintStore) {
+ ['$scope','$location','$window','egCore',
+ function($scope , $location , $window , egCore) {
$scope.focusMe = true;
- egPrintStore.getItem('eg.conf.workstation.all')
+ egCore.hatch.getItem('eg.conf.workstation.all')
.then(function(all) {
if (all && all.length) {
$scope.workstations = all.map(function(a) { return a.name });
}
} else {
// no workstation requested; use the default
- egPrintStore.getItem('eg.conf.workstation.default')
+ egCore.hatch.getItem('eg.conf.workstation.default')
.then(function(ws) {
$scope.args = {workstation : ws}
});
if (! (args.username && args.password) ) return;
args.type = 'staff';
- egAuth.login(args).then(
+ egCore.auth.login(args).then(
function() {
// after login, send the user back to the originally
* data for each so that data reloads are not needed on every
* tab click (i.e. route persistence).
*/
-.factory('bucketSvc',
- ['$q','egNet','egAuth','egIDL','egEvent',
-function($q, egNet, egAuth, egIDL, egEvent) {
+.factory('bucketSvc', ['$q','egCore', function($q, egCore) {
var service = {
allBuckets : [], // un-fleshed user buckets
fetchUserBuckets : function(force) {
if (this.allBuckets.length && !force) return;
var self = this;
- return egNet.request(
+ return egCore.net.request(
'open-ils.actor',
'open-ils.actor.container.retrieve_by_class.authoritative',
- egAuth.token(), egAuth.user().id(),
+ egCore.auth.token(), egCore.auth.user().id(),
'biblio', 'staff_client'
).then(function(buckets) { self.allBuckets = buckets });
},
createBucket : function(name, desc) {
var deferred = $q.defer();
- var bucket = new egIDL.cbreb();
- bucket.owner(egAuth.user().id());
+ var bucket = new egCore.idl.cbreb();
+ bucket.owner(egCore.auth.user().id());
bucket.name(name);
bucket.description(desc || '');
bucket.btype('staff_client');
- egNet.request(
+ egCore.net.request(
'open-ils.actor',
'open-ils.actor.container.create',
- egAuth.token(), 'biblio', bucket
+ egCore.auth.token(), 'biblio', bucket
).then(function(resp) {
if (resp) {
if (typeof resp == 'object') {
bucket.name(args.name);
bucket.description(args.desc);
bucket.pub(args.pub);
- return egNet.request(
+ return egCore.net.request(
'open-ils.actor',
'open-ils.actor.container.update',
- egAuth.token(), 'biblio', bucket
+ egCore.auth.token(), 'biblio', bucket
);
}
}
var deferred = $q.defer();
- egNet.request(
+ egCore.net.request(
'open-ils.actor',
'open-ils.actor.container.flesh.authoritative',
- egAuth.token(), 'biblio', id
+ egCore.auth.token(), 'biblio', id
).then(function(bucket) {
- var evt = egEvent.parse(bucket);
+ var evt = egCore.evt.parse(bucket);
if (evt) {
console.debug(evt);
deferred.reject(evt);
// promise is rejected on failure
service.detachRecord = function(itemId) {
var deferred = $q.defer();
- egNet.request(
+ egCore.net.request(
'open-ils.actor',
'open-ils.actor.container.item.delete',
- egAuth.token(), 'biblio', itemId
+ egCore.auth.token(), 'biblio', itemId
).then(function(resp) {
- var evt = egEvent.parse(resp);
+ var evt = egCore.evt.parse(resp);
if (evt) {
console.error(evt);
deferred.reject(evt);
// rejected otherwise.
service.deleteBucket = function(id) {
var deferred = $q.defer();
- egNet.request(
+ egCore.net.request(
'open-ils.actor',
'open-ils.actor.container.full_delete',
- egAuth.token(), 'biblio', id
+ egCore.auth.token(), 'biblio', id
).then(function(resp) {
- var evt = egEvent.parse(resp);
+ var evt = egCore.evt.parse(resp);
if (evt) {
console.error(evt);
deferred.reject(evt);
*/
.controller('RecordBucketCtrl',
['$scope','$location','$q','$timeout','$modal',
- '$window','egAuth','bucketSvc','egNet','egIDL',
+ '$window','egCore','bucketSvc',
function($scope, $location, $q, $timeout, $modal,
- $window, egAuth, bucketSvc, egNet, egIDL) {
+ $window, egCore, bucketSvc) {
$scope.bucketSvc = bucketSvc;
$scope.bucket = function() { return bucketSvc.currentBucket }
angular.forEach(recs,
function(rec) {
- var item = new egIDL.cbrebi();
+ var item = new egCore.idl.cbrebi();
item.bucket(bucketSvc.currentBucket.id());
item.target_biblio_record_entry(rec.id);
- egNet.request(
+ egCore.net.request(
'open-ils.actor',
'open-ils.actor.container.item.create',
- egAuth.token(), 'biblio', item
+ egCore.auth.token(), 'biblio', item
).then(function(resp) {
// HACK: add the IDs of the added items so that the size
// TODO: improve auth cookie handling so this isn't necessary.
// today the cookie path is too specific (/eg/staff) for non-staff
// UIs to access it. See services/auth.js
- url += '&ses=' + egAuth.token();
+ url += '&ses=' + egCore.auth.token();
$timeout(function() { $window.open(url) });
});
}])
.controller('SearchCtrl',
- ['$scope','$routeParams','egAuth','egNet','bucketSvc',
-function($scope, $routeParams, egAuth, egNet, bucketSvc) {
+ ['$scope','$routeParams','egCore','bucketSvc',
+function($scope, $routeParams, egCore , bucketSvc) {
$scope.setTab('search');
$scope.focusMe = true;
$scope.searchInProgress = true;
bucketSvc.queryRecords = [];
- egNet.request(
+ egCore.net.request(
'open-ils.search',
'open-ils.search.biblio.multiclass.query', {
limit : 500 // meh
/**
* checkin service
*/
-.factory('checkinSvc',
- ['$q','egNet','egAuth','egUser','egEnv','egOrg',
-function($q, egNet, egAuth, egUser, egEnv, egOrg) {
+.factory('checkinSvc', [function() {
var service = {};
service.checkins = [];
/**
* locally cached org unit addresses -- needed for route slips, etc.
*/
-.factory('orgAddrSvc',
- ['$q','egOrg','egPCRUD',
-function($q, egOrg, egPCRUD) {
+.factory('orgAddrSvc', ['$q','egCore', function($q, egCore) {
var service = {cache : {}};
service.getAddr = function(org_id, addr_type) {
if (service.cache[org_id]) {
}
var deferred = $q.defer();
- egPCRUD.retrieve('aoa', egOrg.get(org_id).holds_address())
+ egCore.pcrud.retrieve('aoa', egCore.org.get(org_id).holds_address())
.then(function(addr) {
service.cache[org_id][addr_type] = addr;
deferred.resolve(addr);
return service;
}])
-
/**
* Manages checkin
*/
.controller('CheckinCtrl',
- ['$scope','$q','$modal','egStartup','checkinSvc','egNet','egAuth','egGridDataProvider',
- 'orgAddrSvc','egOrg','egPCRUD','egAlertDialog','egConfirmDialog','egCheckinStrings',
-function($scope, $q, $modal, egStartup, checkinSvc, egNet , egAuth , egGridDataProvider,
- orgAddrSvc, egOrg, egPCRUD, egAlertDialog, egConfirmDialog, egCheckinStrings) {
+ ['$scope','$q','$modal','egCore','checkinSvc','egGridDataProvider',
+ 'orgAddrSvc','egAlertDialog','egConfirmDialog',
+function($scope, $q, $modal, egCore, checkinSvc, egGridDataProvider,
+ orgAddrSvc, egAlertDialog, egConfirmDialog) {
- // run egStartup here since it's not handled via resolver
- egStartup.go().then(
+ // run egCore.startup here since it's not handled via resolver
+ egCore.startup.go().then(
function() {
// handle post-startup business
}
var method = 'open-ils.circ.checkin';
if (override) method += '.override';
- egNet.request('open-ils.circ', method, egAuth.token(), args)
+ egCore.net.request('open-ils.circ', method, egCore.auth.token(), args)
.then(function(evt) {
if (!evt) {
addCheckin(evt);
if (copy.status() == 8) { // on holds shelf
if (hold &&
- hold.pickup_lib() == egAuth.user().ws_ou()) {
+ hold.pickup_lib() == egCore.auth.user().ws_ou()) {
// item should be on our holds shelf.
// display the holds slip
openRouteDialog(
break;
case 'ASSET_COPY_NOT_FOUND':
$scope.blurMe = true;
- egAlertDialog.open(egCheckinStrings.UNCAT_ALERT_DIALOG, args)
+ egAlertDialog.open(egCore.strings.UNCAT_ALERT_DIALOG, args)
.result.then(function() {$scope.focusMe = true});
break;
case 'COPY_ALERT_MESSAGE':
$scope.blurMe = true;
egConfirmDialog.open(
- egCheckinStrings.COPY_ALERT_MSG_DIALOG_TITLE,
+ egCore.strings.COPY_ALERT_MSG_DIALOG_TITLE,
evt.payload, // payload == alert message text
{ copy_barcode : args.copy_barcode,
ok : function() {
function($scope, $modalInstance, destAddr, holdUser) {
$scope.destAddr = destAddr;
$scope.holdUser = holdUser;
- $scope.dest = egOrg.get(evt.org);
+ $scope.dest = egCore.org.get(evt.org);
$scope.evt = evt;
$scope.now = new Date();
$scope.ok = function() {$modalInstance.close()}
holdUser : function() {
// TODO SERVER: response payload should flesh hold recipient
if (!evt.payload.hold) return $q.when();
- return egPCRUD.retrieve('au',
+ return egCore.pcrud.retrieve('au',
evt.payload.hold.usr(), {
flesh : 1,
flesh_fields : {'au' : ['card']}
// data loaded at startup which only requires an authtoken goes
// here. this allows the requests to be run in parallel instead of
// waiting until startup has completed.
- var resolver = {delay : [
- 'egAuth','egUser','egNet','egEnv','egPCRUD','egStartup','egOrg',
- function(egAuth , egUser , egNet , egEnv , egPCRUD , egStartup , egOrg) {
+ var resolver = {delay : ['egCore','egUser', function(egCore , egUser) {
// fetch the org settings we care about during egStartup
- // and toss them into egEnv as egEnv.aous[name] = value.
- egEnv.classLoaders.aous = function() {
- return egOrg.settings(['circ.obscure_dob'])
- .then(function(settings) { egEnv.aous = settings });
+ // and toss them into egCore.env as egCore.env.aous[name] = value.
+ egCore.env.classLoaders.aous = function() {
+ return egCore.org.settings(['circ.obscure_dob'])
+ .then(function(settings) { egCore.env.aous = settings });
}
- egEnv.classLoaders.pgt = function() {
- return egPCRUD.search('pgt', {parent : null},
+ egCore.env.classLoaders.pgt = function() {
+ return egCore.pcrud.search('pgt', {parent : null},
{flesh : -1, flesh_fields : {pgt : ['children']}}
).then(
- function(tree) {egEnv.absorbTree(tree, 'pgt')}
+ function(tree) {egCore.env.absorbTree(tree, 'pgt')}
);
}
- egEnv.loadClasses.push('aous');
- egEnv.loadClasses.push('pgt');
+ egCore.env.loadClasses.push('aous');
+ egCore.env.loadClasses.push('pgt');
// app-globally modify the default flesh fields for
// fleshed user retrieval
egUser.defaultFleshFields.push('ident_type');
egUser.defaultFleshFields.push('ident_type2');
- return egStartup.go()
+ return egCore.startup.go()
}]};
$routeProvider.when('/circ/patron/search', {
* Patron service
*/
.factory('patronSvc',
- ['$q','$timeout','egNet','egAuth','egUser','egEnv','egOrg',
-function($q , $timeout , egNet, egAuth, egUser, egEnv, egOrg) {
+ ['$q','$timeout','egCore','egUser',
+function($q , $timeout , egCore, egUser) {
var service = {
// currently selected patron object
// flesh some additional user fields locally
service.localFlesh = function(user) {
if (typeof user.home_ou() != 'object')
- user.home_ou(egOrg.get(user.home_ou()));
+ user.home_ou(egCore.org.get(user.home_ou()));
angular.forEach(
user.standing_penalties(),
function(penalty) {
if (typeof penalty.org_unit() != 'object')
- penalty.org_unit(egOrg.get(penalty.org_unit()));
+ penalty.org_unit(egCore.org.get(penalty.org_unit()));
}
);
}
// grab additional circ info
service.fetchUserStats = function() {
- egNet.request(
+ egCore.net.request(
'open-ils.actor',
'open-ils.actor.user.opac.vital_stats',
- egAuth.token(), service.current.id()
+ egCore.auth.token(), service.current.id()
).then(
function(stats) {
// force numeric to ensure correct boolean handling in templates
* Manages tabbed patron view
* */
.controller('PatronCtrl',
- ['$scope','$q','$filter','egNet','egAuth','egUser','patronSvc','egEnv','egIDL',
-function($scope, $q, $filter, egNet, egAuth, egUser, patronSvc, egEnv, egIDL) {
+ ['$scope','$q','$filter','egCore','egUser','patronSvc',
+function($scope, $q, $filter, egCore, egUser, patronSvc) {
// called after each route-specified controller is instantiated.
// this doubles as a way to inform the top-level controller that
$scope.initTab = function(tab, patron_id) {
console.log('init tab ' + tab);
$scope.tab = tab;
- $scope.aous = egEnv.aous;
+ $scope.aous = egCore.env.aous;
if (patron_id) {
$scope.patron_id = patron_id
patronSvc.setDefault($scope.patron_id);
}])
.controller('PatronBarcodeSearchCtrl',
- ['$scope','$location','egCore','egConfirmDialog','egUser','egAppStrings','patronSvc',
-function($scope , $location , egCore , egConfirmDialog , egUser , egAppStrings , patronSvc) {
+ ['$scope','$location','egCore','egConfirmDialog','egUser','patronSvc',
+function($scope , $location , egCore , egConfirmDialog , egUser , patronSvc) {
$scope.selectMe = true; // focus text input
patronSvc.setDefault(); // clear the default user
.then(function(user) { // retrieve user
egConfirmDialog.open(
- egAppStrings.OPT_IN_DIALOG, '',
+ egCore.strings.OPT_IN_DIALOG, '',
{ org : egCore.org.get(user.home_ou()),
user : user,
ok : function() { createOptIn(user.id()) },
* Manages patron search
*/
.controller('PatronSearchCtrl',
- ['$scope','$q','$routeParams','$timeout','$window','$location','egEnv',
- '$filter','egIDL','egNet','egAuth','egEvent','egUser',
- 'patronSvc','egGridDataProvider','egPrintStore','egOrg',
-function($scope, $q, $routeParams, $timeout, $window, $location, egEnv,
- $filter, egIDL, egNet, egAuth, egEvent, egUser,
- patronSvc , egGridDataProvider , egPrintStore , egOrg) {
+ ['$scope','$q','$routeParams','$timeout','$window','$location','egCore',
+ '$filter','egUser', 'patronSvc','egGridDataProvider',
+function($scope, $q, $routeParams, $timeout, $window, $location, egCore,
+ $filter, egUser, patronSvc , egGridDataProvider) {
$scope.initTab('search');
$scope.focusMe = true;
fieldValueCache = {};
patronSvc.patrons = [];
- egNet.request(
+ egCore.net.request(
'open-ils.actor',
'open-ils.actor.patron.search.advanced.fleshed',
- egAuth.token(),
+ egCore.auth.token(),
fullSearch.search,
fullSearch.count,
fullSearch.sort,
fullSearch.inactive,
- fullSearch.home_ou || egOrg.tree().id(),
+ fullSearch.home_ou || egCore.org.tree().id(),
egUser.defaultFleshFields,
fullSearch.offset
// typeahead doesn't filter correctly with full hash objects, so
// trim them down to just name and id. This would allow us to use
- // full objects (egIDL.toHash()) instead of manually creating
+ // full objects (egCore.idl.toHash()) instead of manually creating
// objects... maybe later.
// http://stackoverflow.com/questions/18429967/angularjs-ui-typeahead-match-on-leading-characters
- $scope.profiles = egEnv.pgt.list.map(
+ $scope.profiles = egCore.env.pgt.list.map(
function(grp) {return {name : grp.name(), id : grp.id() }})
- $scope.org_units = egEnv.aou.list.map(
+ $scope.org_units = egCore.env.aou.list.map(
function(org) {return {shortname : org.shortname(), id : org.id() }})
// TODO:
/** * Manages patron summary view
*/
.controller('PatronSummaryCtrl',
- ['$scope','$q','egNet','egAuth','egEvent','patronSvc',
-function($scope, $q, egNet, egAuth, egEvent, patronSvc) {
+ ['$scope',
+function($scope) {
// so for, all data for this controller is data inherited
// from the parent scope. Nothing to do here.
}])
* Manages edit
*/
.controller('PatronBillsCtrl',
- ['$scope','$q','$routeParams','egNet','egAuth','egUser','patronSvc',
-function($scope, $q, $routeParams, egNet, egAuth, egUser, patronSvc) {
+ ['$scope','$routeParams','egCore',
+function($scope, $routeParams, egCore) {
$scope.initTab('bills', $routeParams.id);
}])
* Manages messages
*/
.controller('PatronMessagesCtrl',
- ['$scope','$q','$routeParams','egNet','egAuth','egUser','patronSvc',
-function($scope, $q, $routeParams, egNet, egAuth, egUser, patronSvc) {
+ ['$scope','$routeParams','egCore',
+function($scope, $routeParams, egCore) {
$scope.initTab('messages', $routeParams.id);
}])
* Manages edit
*/
.controller('PatronEditCtrl',
- ['$scope','$q','$routeParams','egNet','egAuth','egUser','patronSvc',
-function($scope, $q, $routeParams, egNet, egAuth, egUser, patronSvc) {
+ ['$scope','$routeParams','egCore',
+function($scope, $routeParams, egCore) {
$scope.initTab('edit', $routeParams.id);
}])
angular.module('egPatronApp').controller('PatronCheckoutCtrl',
- ['$scope','$q','$modal','$routeParams','egNet','egAuth','egUser',
- 'egIDL','patronSvc','egEnv','egPCRUD','egOrg','egGridDataProvider',
+ ['$scope','$q','$modal','$routeParams','egCore','egUser','patronSvc',
+ 'egGridDataProvider',
-function($scope, $q, $modal, $routeParams, egNet, egAuth, egUser,
- egIDL, patronSvc, egEnv, egPCRUD, egOrg , egGridDataProvider) {
+function($scope, $q, $modal, $routeParams, egCore, egUser, patronSvc ,
+ egGridDataProvider) {
$scope.initTab('checkout', $routeParams.id);
// -----------------------------
$scope.selectedNcType = function() {
- if (!egEnv.cnct) return null; // too soon
- var type = egEnv.cnct.map[$scope.checkoutArgs.noncat_type];
+ if (!egCore.env.cnct) return null; // too soon
+ var type = egCore.env.cnct.map[$scope.checkoutArgs.noncat_type];
return type ? type.name() : null;
}
- if (egEnv.cnct) {
- $scope.nonCatTypes = egEnv.cnct.list;
+ if (egCore.env.cnct) {
+ $scope.nonCatTypes = egCore.env.cnct.list;
} else {
- egPCRUD.search('cnct',
- {owning_lib : egOrg.fullPath(egAuth.user().ws_ou(), true)},
+ egCore.pcrud.search('cnct',
+ {owning_lib : egCore.org.fullPath(egCore.auth.user().ws_ou(), true)},
null, {atomic : true}
).then(function(list) {
- egEnv.absorbList(list, 'cnct');
+ egCore.env.absorbList(list, 'cnct');
$scope.nonCatTypes = list
});
}
- egPCRUD.retrieveAll('ccm', null, {atomic : true}).then(
+ egCore.pcrud.retrieveAll('ccm', null, {atomic : true}).then(
function(list) { $scope.circModifiers = list });
// TODO: apply correct response order
args.patron_id = $scope.patron_id;
- egNet.request(
- 'open-ils.circ', method, egAuth.token(), args
+ egCore.net.request(
+ 'open-ils.circ', method, egCore.auth.token(), args
).then(function(evt) {
if (!evt) {
};
} else if (args.noncat) {
evt.payload.record = {
- title : egEnv.cnct.map[args.noncat_type].name()
+ title : egCore.env.cnct.map[args.noncat_type].name()
};
evt.noncat_count = args.noncat_count;
- evt.payload.circ = new egIDL.circ();
+ evt.payload.circ = new egCore.idl.circ();
evt.payload.circ.due_date(evt.payload.noncat_circ.duedate());
}
}
function openNoncatDialog(coArgs) {
coArgs.noncat = true;
- var type = egEnv.cnct.map[coArgs.noncat_type];
+ var type = egCore.env.cnct.map[coArgs.noncat_type];
$modal.open({
templateUrl: './circ/patron/t_noncat_dialog',
angular.module('egPatronApp').controller('PatronHoldsCtrl',
- ['$scope','$q','$routeParams','egNet','egAuth','egUser','patronSvc','egOrg','egGridDataProvider',
-function($scope, $q, $routeParams, egNet, egAuth, egUser, patronSvc, egOrg , egGridDataProvider) {
+ ['$scope','$q','$routeParams','egCore','egUser','patronSvc','egGridDataProvider',
+function($scope, $q, $routeParams, egCore, egUser, patronSvc, egGridDataProvider) {
$scope.initTab('holds', $routeParams.id);
var provider = egGridDataProvider.instance({});
var ids = patronSvc.hold_ids.slice(offset, offset + count);
- return egNet.request(
+ return egCore.net.request(
'open-ils.circ',
'open-ils.circ.hold.details.batch.retrieve.authoritative',
- egAuth.token(), ids
+ egCore.auth.token(), ids
).then(null, null, function(hold_data) {
$scope.loading = false;
var hold = hold_data.hold;
hold_data.id = hold.id();
- hold.pickup_lib(egOrg.get(hold.pickup_lib())); // flesh
+ hold.pickup_lib(egCore.org.get(hold.pickup_lib())); // flesh
patronSvc.holds.push(hold_data);
return hold_data;
});
var deferred = $q.defer();
patronSvc.hold_ids = [];
- egNet.request(
+ egCore.net.request(
'open-ils.circ',
'open-ils.circ.holds.id_list.retrieve.authoritative',
- egAuth.token(), $scope.patron_id
+ egCore.auth.token(), $scope.patron_id
).then(function(hold_ids) {
console.log('fetched hold ids ' + hold_ids);
angular.module('egPatronApp').controller('PatronItemsOutCtrl',
- ['$scope','$q','$routeParams','egNet','egAuth','egUser','patronSvc','egPCRUD','egOrg','egGridDataProvider',
-function($scope, $q, $routeParams, egNet, egAuth, egUser, patronSvc, egPCRUD, egOrg , egGridDataProvider) {
+ ['$scope','$q','$routeParams','egCore','egUser','patronSvc','egGridDataProvider',
+function($scope, $q, $routeParams, egCore , egUser, patronSvc , egGridDataProvider) {
$scope.initTab('items_out', $routeParams.id);
function fetchCircs(offset, count) {
// fetch the lot of circs and stream the results back via notify
- return egPCRUD.search('circ',
+ return egCore.pcrud.search('circ',
{id : patronSvc.items_out_ids},
{ flesh : 4,
flesh_fields : {
order_by : {circ : ['xact_start']}
}).then(null, null, function(circ) {
- circ.circ_lib(egOrg.get(circ.circ_lib())); // local fleshing
+ circ.circ_lib(egCore.org.get(circ.circ_lib())); // local fleshing
if (circ.target_copy().call_number().id() == -1) {
// dummy-up a record for precat items
// avoid returning the request directly so the caller so the
- // notify()'s from egNet.request don't leak into the
+ // notify()'s from egCore.net.request don't leak into the
// final set of notifies (i.e. the real responses);
var deferred = $q.defer();
patronSvc.items_out_ids = [];
- egNet.request(
+ egCore.net.request(
'open-ils.actor',
'open-ils.actor.user.checked_out.authoritative',
- egAuth.token(), $scope.patron_id
+ egCore.auth.token(), $scope.patron_id
).then(function(outs) {
/**
* Free-floating controller which can be used by any app.
*/
-function NavCtrl($scope, $window, $location, egStartup, egAuth, egEnv) {
+function NavCtrl($scope, $window, $location, egCore) {
$scope.applyLocale = function(locale) {
// EGWeb.pm can change the locale for us w/ the right param
// tied to logout link
$scope.logout = function() {
- egAuth.logout();
+ egCore.auth.logout();
return true;
};
if ($location.path() != '/login') {
// avoiding rending user info on the login page
- egStartup.go().then(
+ egCore.startup.go().then(
function() {
- $scope.username = egAuth.user().usrname();
- $scope.workstation = egAuth.workstation();
+ $scope.username = egCore.auth.user().usrname();
+ $scope.workstation = egCore.auth.workstation();
}
);
}
}
// minify-safe dependency injection
-NavCtrl.$inject = ['$scope', '$window',
- '$location', 'egStartup', 'egAuth', 'egEnv'];
+NavCtrl.$inject = ['$scope', '$window', '$location', 'egCore'];
.constant('EG_AUTH_COOKIE', 'ses')
.factory('egAuth',
- ['$q','$cookies','egNet','EG_AUTH_COOKIE','egPrintStore',
-function($q , $cookies , egNet , EG_AUTH_COOKIE , egPrintStore) {
+ ['$q','$cookies','egNet','EG_AUTH_COOKIE','egHatch',
+function($q , $cookies , egNet , EG_AUTH_COOKIE , egHatch) {
var service = {
// the currently active user (au) object
// user previously logged in with a workstation.
// Find the workstation name from the list
// of configured workstations
- egPrintStore.getItem('eg.conf.workstation.all')
+ egHatch.getItem('eg.conf.workstation.all')
.then(function(all) {
if (all) {
var ws = all.filter(
return $q.when(response);
}
- ws_ou = egAuth.user().ws_ou();
+ ws_ou = Number(egAuth.user().ws_ou()); // from string
return service.hasPermAt(permList, true)
.then(function(orgMap) {
.factory('egCore',
['egIDL','egNet','egEnv','egOrg','egPCRUD','egEvent','egAuth',
- 'egPerm','egPrintStore','egStartup',
+ 'egPerm','egHatch','egStartup','egStrings',
function(egIDL , egNet , egEnv , egOrg , egPCRUD , egEvent , egAuth ,
- egPerm , egPrintStore , egStartup) {
+ egPerm , egHatch , egStartup , egStrings) {
return {
idl : egIDL,
evt : egEvent,
auth : egAuth,
perm : egPerm,
- hatch : egPrintStore,
- startup : egStartup
+ hatch : egHatch,
+ startup : egStartup,
+ strings : egStrings
};
}]);
controller : [
'$scope','egIDL','egAuth','egNet', 'egGridFlatDataProvider',
- 'egGridColumnsProvider','$filter','$window','egPrintStore',
+ 'egGridColumnsProvider','$filter','$window','egHatch',
function($scope, egIDL, egAuth, egNet, egGridFlatDataProvider,
- egGridColumnsProvider , $filter , $window , egPrintStore) {
+ egGridColumnsProvider , $filter , $window , egHatch) {
var grid = this;
}
$scope.printCSV = function() {
- egPrintStore.print('text/plain', grid.generateCSV())
+ egHatch.print('text/plain', grid.generateCSV())
.then(function() { console.debug('print complete') });
}
--- /dev/null
+/**
+ * Core Service - egHatch
+ *
+ * Dispatches print and data storage requests to the appropriate handler.
+ *
+ * With each top-level request, if a connection to Hatch is established,
+ * the request is relayed. If a connection has not been attempted, an
+ * attempt is made then the request is handled. If Hatch is known to be
+ * inaccessible, requests are routed to local handlers.
+ *
+ * Most handlers also provide direct remote and local variants to the
+ * application can decide to which to use as needed.
+ *
+ * Printing is handled locally with an egPrintContainer whose contents
+ * are made visible during printing using CSS print media.
+ *
+ * Local storage requests are handled by $window.localStorage.
+ *
+ * Note that all top-level and remote requests return promises. All
+ * local requests return immediate values, since local requests are
+ * never asynchronous.
+ *
+ * BEWARE: never store "fieldmapper" objects, since their structure
+ * may change over time as the IDL changes. Always flatten objects
+ * into key/value pairs before calling set*Item()
+ *
+ */
+angular.module('egCoreMod')
+
+.factory('egHatch',
+ ['$q','$window','$timeout','$interpolate','$rootScope',
+ function($q , $window , $timeout , $interpolate , $rootScope) {
+
+ var service = {};
+ service.msgId = 0;
+ service.messages = {};
+ service.pending = [];
+ service.socket = null;
+ service.hatchAvailable = null;
+ service.defaultHatchURL = 'wss://localhost:8443/hatch';
+ service.hatchRequired = false;
+
+ // write a message to the Hatch websocket
+ service.sendToHatch = function(msg) {
+ var msg2 = {};
+
+ // shallow copy and scrub msg before sending
+ angular.forEach(msg, function(val, key) {
+ if (key.match(/deferred/)) return;
+ msg2[key] = val;
+ });
+
+ console.debug("sending to Hatch: " + JSON.stringify(msg2,null,2));
+ service.socket.send(JSON.stringify(msg2));
+ }
+
+ // Send the request to Hatch if it's available.
+ // Otherwise handle the request locally.
+ service.attemptHatchDelivery = function(msg) {
+
+ msg.msgid = service.msgId++;
+ msg.deferred = $q.defer();
+ service.messages[msg.msgid] = msg;
+
+ if (service.hatchAvailable === false) { // Hatch is closed
+ msg.deferred.reject(msg);
+
+ } else if (service.hatchAvailable === true) { // Hatch is open
+ // Hatch is known to be open
+ service.sendToHatch(msg);
+
+ } else { // Hatch status unknown; attempt to connect
+ service.pending.push(msg);
+ service.hatchConnect();
+ }
+
+ return msg.deferred.promise;
+ }
+
+
+ // resolve the promise on the given request and remove
+ // it from our tracked requests.
+ service.resolveRequest = function(msg) {
+
+ if (!service.messages[msg.msgid]) {
+ console.warn('no cached message for '
+ + msg.msgid + ' : ' + JSON.stringify(msg, null, 2));
+ return;
+ }
+
+ // for requests sent through Hatch, only the cached
+ // request will have the original promise attached
+ msg.deferred = service.messages[msg.msgid].deferred;
+ delete service.messages[msg.msgid]; // un-cache
+
+ // resolve / reject
+ if (msg.error) {
+ throw new Error(
+ "egHatch command failed : "
+ + JSON.stringify(msg.error, null, 2));
+ } else {
+ msg.deferred.resolve(msg.content);
+ }
+ }
+
+ // pass each request off to its local handler function
+ service.handleRequestsLocally = function() {
+ service.socket = null;
+ service.hatchAvailable = false;
+
+ while ( (msg = service.pending.shift()) ) {
+
+ if (service.hatchRequired) {
+ throw new Error(
+ "egHatch : attempt to perform '" + msg.action +
+ "' operation failed because no connection to Hatch could be " +
+ "established and egHatch.hatchRequired is set to TRUE"
+ );
+ }
+
+ if (msg.localHandler) {
+ msg.content = msg.localHandler(msg);
+ } else {
+ msg.error =
+ 'no fall-thru handler for requested action: '
+ + msg.action;
+ }
+
+ service.resolveRequest(msg);
+ }
+ }
+
+ service.hatchClosed = function() {
+ console.debug("Hatch closing...");
+ service.socket = null;
+ service.printers = [];
+ service.printConfig = {};
+ while ( (msg = service.pending.shift()) )
+ msg.deferred.reject(msg);
+ if (service.onHatchClose)
+ service.onHatchClose();
+ }
+
+ service.hatchURL = function() {
+ return service.getLocalItem('eg.conf.hatch.url')
+ || service.defaultHatchURL;
+ }
+
+ service.hatchConnect = function() {
+
+ if (service.socket &&
+ service.socket.readyState == service.socket.CONNECTING) {
+ // connection in progress. Nothing to do. Our queued
+ // message will be delivered when onopen() fires
+ return;
+ }
+
+ console.debug("connecting to Hatch...");
+
+ try {
+ service.socket = new WebSocket(service.hatchURL());
+ } catch(e) {
+ service.hatchAvailable = false;
+ service.hatchClosed();
+ return;
+ }
+
+ service.socket.onopen = function() {
+ console.debug('connected to Hatch');
+ service.hatchAvailable = true;
+ if (service.onHatchOpen)
+ service.onHatchOpen();
+ while ( (msg = service.pending.shift()) ) {
+ service.sendToHatch(msg);
+ };
+ }
+
+ service.socket.onclose = function() {
+ if (service.hatchAvailable === false) return; // already registered
+
+ // onclose() will be called regularly as we disconnect from
+ // Hatch via timeouts. Return hatchAvailable to its unknow state
+ service.hatchAvailable = null;
+ service.hatchClosed();
+ }
+
+ service.socket.onerror = function() {
+ if (service.hatchAvailable === false) return; // already registered
+ service.hatchAvailable = false;
+ console.debug(
+ "unable to connect to Hatch server at " + service.hatchURL());
+ service.hatchClosed();
+ }
+
+ service.socket.onmessage = function(evt) {
+ var msgStr = evt.data;
+ if (!msgStr) throw new Error("Hatch returned empty message");
+
+ var msgObj = JSON.parse(msgStr);
+ console.debug('Hatch says ' + JSON.stringify(msgObj, null, 2));
+ service.resolveRequest(msgObj);
+ }
+ }
+
+ service.interpolateHtmlTemplate = function(template, printScope) {
+ // TODO: for print template security, we must scrub
+ // the scope object and remove any references to
+ // functions or objects. Otherwise, print templates
+ // would have the power to modify data via the scope
+ var subScope = $rootScope.$new();
+ angular.forEach(printScope, function(val, key) {subScope[key] = val});
+ var html = $interpolate(template)(subScope);
+ subScope.$destroy();
+ return html;
+ }
+
+
+ // supported values for contentType are 'text/html' and 'text/plain'
+ service.print = function(
+ context, contentType, content, printScope, withDialog) {
+
+ // generate our HTML content if necessary
+ if (contentType == 'text/html') {
+ content = service.interpolateHtmlTemplate(content, printScope);
+ console.debug('generated HTML ' + content);
+ }
+
+ return service.remotePrint(
+ context, contentType, content, printScope, withDialog)['catch'](
+ function(msg) {
+ // remote print not available; print locally
+ return service.browserPrint(msg);
+ }
+ );
+ }
+
+ service.remotePrint = function(
+ context, contentType, content, printScope, withDialog) {
+
+ return service.getPrintConfig().then(
+ function(conf) {
+ // print configuration retrieved; print
+ return service.attemptHatchDelivery({
+ action : 'print',
+ config : conf[context],
+ content : content,
+ contentType : contentType,
+ showDialog : withDialog,
+ });
+ }
+ );
+ }
+
+ // print locally via the browser
+ service.browserPrint = function(msg) {
+ service.onBrowserPrint(msg.contentType, msg.content);
+ }
+
+ // -------------
+ // print configuration is always stored as remote items,
+ // since there is no concept of a local printer
+ service.getPrintConfig = function() {
+ if (service.printConfig)
+ return $q.when(service.printConfig);
+
+ return service.getRemoteItem('eg.printing.config')
+ .then(function(conf) {
+ return (service.printConfig = conf || {})
+ });
+ }
+ service.setPrintConfig = function(conf) {
+ service.printConfig = conf;
+ return service.setRemoteItem('eg.printing.config', conf);
+ }
+ // -----------
+
+ // launch the print dialog then attach the resulting configuration
+ // to the requested context, then store the final values.
+ service.configurePrinter = function(context, printer) {
+
+ // load current settings
+ return service.getPrintConfig()
+
+ // dispatch the print configuration request
+ .then(function(config) {
+
+ // loaded remote config
+ if (!config[context]) config[context] = {};
+ config[context].printer = printer;
+ return service.attemptHatchDelivery({
+ key : 'no-op',
+ action : 'print-config',
+ config : config[context]
+ })
+ })
+
+ // set the returned settings to the requested context
+ .then(function(newconf) {
+ if (angular.isObject(newconf)) {
+ newconf.printer = printer;
+ return service.printConfig[context] = newconf;
+ } else {
+ console.warn("configurePrinter() returned " + newconf);
+ }
+ })
+
+ // store the newly linked settings
+ .then(function() {
+ service.setItem('eg.printing.config', service.printConfig);
+ })
+
+ // return the final settings to the caller
+ .then(function() {return service.printConfig});
+ }
+
+ service.getPrinters = function() {
+ if (service.printers) // cached printers
+ return $q.when(service.printers);
+
+ return service.attemptHatchDelivery({action : 'printers'}).then(
+
+ // we have remote printers; sort by name and return
+ function(printers) {
+ service.printers = printers.sort(
+ function(a,b) {return a.name < b.name ? -1 : 1});
+ return service.printers;
+ },
+
+ // remote call failed and there is no such thing as local
+ // printers; return empty set.
+ function() { return [] }
+ );
+ }
+
+ // get the value for a stored item
+ service.getItem = function(key) {
+ return service.getRemoteItem(key)['catch'](
+ function(msg) {
+ return service.getLocalItem(msg.key);
+ }
+ );
+ }
+
+ service.getRemoteItem = function(key) {
+ return service.attemptHatchDelivery({
+ key : key,
+ action : 'get',
+ });
+ }
+
+ service.getLocalItem = function(key) {
+ var val = $window.localStorage.getItem(key);
+ if (val == null) return;
+ return JSON.parse(val);
+ }
+
+ service.setItem = function(key, value) {
+ var str = JSON.stringify(value);
+ return service.setRemoteItem(key, str)['catch'](
+ function(msg) {
+ return service.setLocalItem(msg.key, str);
+ }
+ );
+ }
+
+ // set the value for a stored or new item
+ service.setRemoteItem = function(key, value) {
+ return service.attemptHatchDelivery({
+ key : key,
+ value : value,
+ action : 'set',
+ });
+ }
+
+ service.setLocalItem = function(key, value) {
+ $window.localStorage.setItem(key, value);
+ }
+
+ // appends the value to the existing item stored at key.
+ // If not item is found at key, this behaves just like setItem()
+ service.appendItem = function(key, value) {
+ return service.appendRemoteItem(key, value)['catch'](
+ function(msg) {
+ return service.appendLocalItem(msg.key, msg.value);
+ }
+ );
+ }
+
+ service.appendRemoteItem = function(key, value) {
+ return service.attemptHatchDelivery({
+ key : key,
+ value : value,
+ action : 'append',
+ });
+ }
+
+ // assumes the appender and appendee are both strings
+ // TODO: support arrays as well
+ service.appendLocalItem = function(key, value) {
+ var item = service.getLocalItem(key);
+ if (item) {
+ if (typeof item != 'string') {
+ logger.warn("egHatch.appendLocalItem => "
+ + "cannot append to a non-string item: " + key);
+ return;
+ }
+ value = item + value; // concatenate our value
+ }
+ service.setLocalitem(key, value);
+ }
+
+ // remove a stored item
+ service.removeItem = function(key) {
+ return service.removeRemoteItem(key)['catch'](
+ function(msg) {
+ return service.removeLocalItem(msg.key)
+ }
+ );
+ }
+
+ service.removeRemoteItem = function(key) {
+ return service.attemptHatchDelivery({
+ key : key,
+ action : 'remove'
+ });
+ }
+
+ service.removeLocalItem = function(key) {
+ $window.localStorage.removeItem(key);
+ }
+
+ // if set, prefix limits the return set to keys starting with 'prefix'
+ service.getKeys = function(prefix) {
+ return service.getRemoteKeys(prefix)['catch'](
+ function() {
+ return service.getLocalKeys(prefix)
+ }
+ );
+ }
+
+ service.getRemoteKeys = function(prefix) {
+ return service.attemptHatchDelivery({
+ key : prefix,
+ action : 'keys'
+ });
+ }
+
+ service.getLocalKeys = function(prefix) {
+ var keys = [];
+ var idx = 0;
+ while ( (k = $window.localStorage.key(idx++)) !== null) {
+ // key prefix match test
+ if (prefix && k.substr(0, prefix.length) != prefix) continue;
+ keys.push(k);
+ }
+ return keys;
+ }
+
+ return service;
+}])
+
+/**
+ * Container for inserting print data into the browser page.
+ * On insert, $window.print() is called to print the data.
+ * The div housing eg-print-container must apply the correct
+ * print media CSS to ensure this content (and not the rest
+ * of the page) is printed.
+ */
+.directive('egPrintContainer', function() {
+ return {
+ restrict : 'AE',
+ template : '<div id="eg-print-container-for-html"></div>',
+ controller :
+ ['$scope','$window','$timeout','egHatch',
+ function($scope , $window , $timeout, egHatch) {
+
+ egHatch.onBrowserPrint = function(contentType, content) {
+ switch(contentType) {
+ case 'text/csv':
+ case 'text/plain':
+ // preserve newlines, spaces, etc.
+ content = '<pre>' + content + '</pre>';
+ case 'text/html':
+ // TODO: make this angular-y
+ var div = document.getElementById('eg-print-container-for-html');
+ while (div.childNodes[0])
+ div.removeChild(div.childNodes[0]);
+ div.innerHTML = content;
+ }
+
+ // force the template to absorb the data before printing
+ // if an apply/digest loop is not already in progress
+ //if (!$scope.$$phase) $scope.$apply();
+ //
+ // TODO: apply() may no longer be necessary
+
+ $timeout(
+ function() {
+ $scope.$apply();
+ $window.print();
+ }
+ );
+ }
+ }
+ ]
+ }
+});
+
+
+++ /dev/null
-/**
- * Core Service - egPrintStore
- *
- * Dispatches print and data storage requests to the appropriate handler.
- *
- * With each top-level request, if a connection to Hatch is established,
- * the request is relayed. If a connection has not been attempted, an
- * attempt is made then the request is handled. If Hatch is known to be
- * inaccessible, requests are routed to local handlers.
- *
- * Most handlers also provide direct remote and local variants to the
- * application can decide to which to use as needed.
- *
- * Printing is handled locally with an egPrintContainer whose contents
- * are made visible during printing using CSS print media.
- *
- * Local storage requests are handled by $window.localStorage.
- *
- * Note that all top-level and remote requests return promises. All
- * local requests return immediate values, since local requests are
- * never asynchronous.
- *
- * BEWARE: never store "fieldmapper" objects, since their structure
- * may change over time as the IDL changes. Always flatten objects
- * into key/value pairs before calling set*Item()
- *
- */
-angular.module('egCoreMod')
-
-.factory('egPrintStore',
- ['$q','$window','$timeout','$interpolate','$rootScope',
- function($q , $window , $timeout , $interpolate , $rootScope) {
-
- var service = {};
- service.msgId = 0;
- service.messages = {};
- service.pending = [];
- service.socket = null;
- service.hatchAvailable = null;
- service.defaultHatchURL = 'wss://localhost:8443/hatch';
- service.hatchRequired = false;
-
- // write a message to the Hatch websocket
- service.sendToHatch = function(msg) {
- var msg2 = {};
-
- // shallow copy and scrub msg before sending
- angular.forEach(msg, function(val, key) {
- if (key.match(/deferred/)) return;
- msg2[key] = val;
- });
-
- console.debug("sending to Hatch: " + JSON.stringify(msg2,null,2));
- service.socket.send(JSON.stringify(msg2));
- }
-
- // Send the request to Hatch if it's available.
- // Otherwise handle the request locally.
- service.attemptHatchDelivery = function(msg) {
-
- msg.msgid = service.msgId++;
- msg.deferred = $q.defer();
- service.messages[msg.msgid] = msg;
-
- if (service.hatchAvailable === false) { // Hatch is closed
- msg.deferred.reject(msg);
-
- } else if (service.hatchAvailable === true) { // Hatch is open
- // Hatch is known to be open
- service.sendToHatch(msg);
-
- } else { // Hatch status unknown; attempt to connect
- service.pending.push(msg);
- service.hatchConnect();
- }
-
- return msg.deferred.promise;
- }
-
-
- // resolve the promise on the given request and remove
- // it from our tracked requests.
- service.resolveRequest = function(msg) {
-
- if (!service.messages[msg.msgid]) {
- console.warn('no cached message for '
- + msg.msgid + ' : ' + JSON.stringify(msg, null, 2));
- return;
- }
-
- // for requests sent through Hatch, only the cached
- // request will have the original promise attached
- msg.deferred = service.messages[msg.msgid].deferred;
- delete service.messages[msg.msgid]; // un-cache
-
- // resolve / reject
- if (msg.error) {
- throw new Error(
- "egPrintStore command failed : "
- + JSON.stringify(msg.error, null, 2));
- } else {
- msg.deferred.resolve(msg.content);
- }
- }
-
- // pass each request off to its local handler function
- service.handleRequestsLocally = function() {
- service.socket = null;
- service.hatchAvailable = false;
-
- while ( (msg = service.pending.shift()) ) {
-
- if (service.hatchRequired) {
- throw new Error(
- "egPrintStore : attempt to perform '" + msg.action +
- "' operation failed because no connection to Hatch could be " +
- "established and egPrintStore.hatchRequired is set to TRUE"
- );
- }
-
- if (msg.localHandler) {
- msg.content = msg.localHandler(msg);
- } else {
- msg.error =
- 'no fall-thru handler for requested action: '
- + msg.action;
- }
-
- service.resolveRequest(msg);
- }
- }
-
- service.hatchClosed = function() {
- console.debug("Hatch closing...");
- service.socket = null;
- service.printers = [];
- service.printConfig = {};
- while ( (msg = service.pending.shift()) )
- msg.deferred.reject(msg);
- if (service.onHatchClose)
- service.onHatchClose();
- }
-
- service.hatchURL = function() {
- return service.getLocalItem('eg.conf.hatch.url')
- || service.defaultHatchURL;
- }
-
- service.hatchConnect = function() {
-
- if (service.socket &&
- service.socket.readyState == service.socket.CONNECTING) {
- // connection in progress. Nothing to do. Our queued
- // message will be delivered when onopen() fires
- return;
- }
-
- console.debug("connecting to Hatch...");
-
- try {
- service.socket = new WebSocket(service.hatchURL());
- } catch(e) {
- service.hatchAvailable = false;
- service.hatchClosed();
- return;
- }
-
- service.socket.onopen = function() {
- console.debug('connected to Hatch');
- service.hatchAvailable = true;
- if (service.onHatchOpen)
- service.onHatchOpen();
- while ( (msg = service.pending.shift()) ) {
- service.sendToHatch(msg);
- };
- }
-
- service.socket.onclose = function() {
- if (service.hatchAvailable === false) return; // already registered
-
- // onclose() will be called regularly as we disconnect from
- // Hatch via timeouts. Return hatchAvailable to its unknow state
- service.hatchAvailable = null;
- service.hatchClosed();
- }
-
- service.socket.onerror = function() {
- if (service.hatchAvailable === false) return; // already registered
- service.hatchAvailable = false;
- console.debug(
- "unable to connect to Hatch server at " + service.hatchURL());
- service.hatchClosed();
- }
-
- service.socket.onmessage = function(evt) {
- var msgStr = evt.data;
- if (!msgStr) throw new Error("Hatch returned empty message");
-
- var msgObj = JSON.parse(msgStr);
- console.debug('Hatch says ' + JSON.stringify(msgObj, null, 2));
- service.resolveRequest(msgObj);
- }
- }
-
- service.interpolateHtmlTemplate = function(template, printScope) {
- // TODO: for print template security, we must scrub
- // the scope object and remove any references to
- // functions or objects. Otherwise, print templates
- // would have the power to modify data via the scope
- var subScope = $rootScope.$new();
- angular.forEach(printScope, function(val, key) {subScope[key] = val});
- var html = $interpolate(template)(subScope);
- subScope.$destroy();
- return html;
- }
-
-
- // supported values for contentType are 'text/html' and 'text/plain'
- service.print = function(
- context, contentType, content, printScope, withDialog) {
-
- // generate our HTML content if necessary
- if (contentType == 'text/html') {
- content = service.interpolateHtmlTemplate(content, printScope);
- console.debug('generated HTML ' + content);
- }
-
- return service.remotePrint(
- context, contentType, content, printScope, withDialog)['catch'](
- function(msg) {
- // remote print not available; print locally
- return service.browserPrint(msg);
- }
- );
- }
-
- service.remotePrint = function(
- context, contentType, content, printScope, withDialog) {
-
- return service.getPrintConfig().then(
- function(conf) {
- // print configuration retrieved; print
- return service.attemptHatchDelivery({
- action : 'print',
- config : conf[context],
- content : content,
- contentType : contentType,
- showDialog : withDialog,
- });
- }
- );
- }
-
- // print locally via the browser
- service.browserPrint = function(msg) {
- service.onBrowserPrint(msg.contentType, msg.content);
- }
-
- // -------------
- // print configuration is always stored as remote items,
- // since there is no concept of a local printer
- service.getPrintConfig = function() {
- if (service.printConfig)
- return $q.when(service.printConfig);
-
- return service.getRemoteItem('eg.printing.config')
- .then(function(conf) {
- return (service.printConfig = conf || {})
- });
- }
- service.setPrintConfig = function(conf) {
- service.printConfig = conf;
- return service.setRemoteItem('eg.printing.config', conf);
- }
- // -----------
-
- // launch the print dialog then attach the resulting configuration
- // to the requested context, then store the final values.
- service.configurePrinter = function(context, printer) {
-
- // load current settings
- return service.getPrintConfig()
-
- // dispatch the print configuration request
- .then(function(config) {
-
- // loaded remote config
- if (!config[context]) config[context] = {};
- config[context].printer = printer;
- return service.attemptHatchDelivery({
- key : 'no-op',
- action : 'print-config',
- config : config[context]
- })
- })
-
- // set the returned settings to the requested context
- .then(function(newconf) {
- if (angular.isObject(newconf)) {
- newconf.printer = printer;
- return service.printConfig[context] = newconf;
- } else {
- console.warn("configurePrinter() returned " + newconf);
- }
- })
-
- // store the newly linked settings
- .then(function() {
- service.setItem('eg.printing.config', service.printConfig);
- })
-
- // return the final settings to the caller
- .then(function() {return service.printConfig});
- }
-
- service.getPrinters = function() {
- if (service.printers) // cached printers
- return $q.when(service.printers);
-
- return service.attemptHatchDelivery({action : 'printers'}).then(
-
- // we have remote printers; sort by name and return
- function(printers) {
- service.printers = printers.sort(
- function(a,b) {return a.name < b.name ? -1 : 1});
- return service.printers;
- },
-
- // remote call failed and there is no such thing as local
- // printers; return empty set.
- function() { return [] }
- );
- }
-
- // get the value for a stored item
- service.getItem = function(key) {
- return service.getRemoteItem(key)['catch'](
- function(msg) {
- return service.getLocalItem(msg.key);
- }
- );
- }
-
- service.getRemoteItem = function(key) {
- return service.attemptHatchDelivery({
- key : key,
- action : 'get',
- });
- }
-
- service.getLocalItem = function(key) {
- var val = $window.localStorage.getItem(key);
- if (val == null) return;
- return JSON.parse(val);
- }
-
- service.setItem = function(key, value) {
- var str = JSON.stringify(value);
- return service.setRemoteItem(key, str)['catch'](
- function(msg) {
- return service.setLocalItem(msg.key, str);
- }
- );
- }
-
- // set the value for a stored or new item
- service.setRemoteItem = function(key, value) {
- return service.attemptHatchDelivery({
- key : key,
- value : value,
- action : 'set',
- });
- }
-
- service.setLocalItem = function(key, value) {
- $window.localStorage.setItem(key, value);
- }
-
- // appends the value to the existing item stored at key.
- // If not item is found at key, this behaves just like setItem()
- service.appendItem = function(key, value) {
- return service.appendRemoteItem(key, value)['catch'](
- function(msg) {
- return service.appendLocalItem(msg.key, msg.value);
- }
- );
- }
-
- service.appendRemoteItem = function(key, value) {
- return service.attemptHatchDelivery({
- key : key,
- value : value,
- action : 'append',
- });
- }
-
- // assumes the appender and appendee are both strings
- // TODO: support arrays as well
- service.appendLocalItem = function(key, value) {
- var item = service.getLocalItem(key);
- if (item) {
- if (typeof item != 'string') {
- logger.warn("egPrintStore.appendLocalItem => "
- + "cannot append to a non-string item: " + key);
- return;
- }
- value = item + value; // concatenate our value
- }
- service.setLocalitem(key, value);
- }
-
- // remove a stored item
- service.removeItem = function(key) {
- return service.removeRemoteItem(key)['catch'](
- function(msg) {
- return service.removeLocalItem(msg.key)
- }
- );
- }
-
- service.removeRemoteItem = function(key) {
- return service.attemptHatchDelivery({
- key : key,
- action : 'remove'
- });
- }
-
- service.removeLocalItem = function(key) {
- $window.localStorage.removeItem(key);
- }
-
- // if set, prefix limits the return set to keys starting with 'prefix'
- service.getKeys = function(prefix) {
- return service.getRemoteKeys(prefix)['catch'](
- function() {
- return service.getLocalKeys(prefix)
- }
- );
- }
-
- service.getRemoteKeys = function(prefix) {
- return service.attemptHatchDelivery({
- key : prefix,
- action : 'keys'
- });
- }
-
- service.getLocalKeys = function(prefix) {
- var keys = [];
- var idx = 0;
- while ( (k = $window.localStorage.key(idx++)) !== null) {
- // key prefix match test
- if (prefix && k.substr(0, prefix.length) != prefix) continue;
- keys.push(k);
- }
- return keys;
- }
-
- return service;
-}])
-
-/**
- * Container for inserting print data into the browser page.
- * On insert, $window.print() is called to print the data.
- * The div housing eg-print-container must apply the correct
- * print media CSS to ensure this content (and not the rest
- * of the page) is printed.
- */
-.directive('egPrintContainer', function() {
- return {
- restrict : 'AE',
- template : '<div id="eg-print-container-for-html"></div>',
- controller :
- ['$scope','$window','$timeout','egPrintStore',
- function($scope , $window , $timeout, egPrintStore) {
-
- egPrintStore.onBrowserPrint = function(contentType, content) {
- switch(contentType) {
- case 'text/csv':
- case 'text/plain':
- // preserve newlines, spaces, etc.
- content = '<pre>' + content + '</pre>';
- case 'text/html':
- // TODO: make this angular-y
- var div = document.getElementById('eg-print-container-for-html');
- while (div.childNodes[0])
- div.removeChild(div.childNodes[0]);
- div.innerHTML = content;
- }
-
- // force the template to absorb the data before printing
- // if an apply/digest loop is not already in progress
- //if (!$scope.$$phase) $scope.$apply();
- //
- // TODO: apply() may no longer be necessary
-
- $timeout(
- function() {
- $scope.$apply();
- $window.print();
- }
- );
- }
- }
- ]
- }
-});
-
-
*
* Displays key information and messages to the user.
*
- * Currently displays network connection status, egPrintStore connection
+ * Currently displays network connection status, egHatch connection
* status, and messages delivered via
* $scope.$emit('egStatusBarMessage', msg)
*/
replace : true,
templateUrl : 'eg-status-bar-template',
controller : [
- '$scope', '$rootScope', 'egPrintStore',
- function($scope, $rootScope, egPrintStore) {
+ '$scope', '$rootScope', 'egHatch',
+ function($scope, $rootScope, egHatch) {
$scope.messages = []; // keep a log of recent messages
$scope.netConnected = function() {
}
$scope.hatchConnected = function() {
- return egPrintStore.hatchAvailable;
+ return egHatch.hatchAvailable;
}
// update the UI whenever we lose connection
- egPrintStore.onHatchClose = function() {
+ egHatch.onHatchClose = function() {
$scope.$apply();
}
// update the UI whenever we lose connection
- egPrintStore.onHatchOpen = function() {
+ egHatch.onHatchOpen = function() {
$scope.$apply();
}
$scope.hatchConnect = function() {
- egPrintStore.hatchConnect();
+ egHatch.hatchConnect();
}
$rootScope.$on('egStatusBarMessage', function(evt, args) {
--- /dev/null
+/**
+ * egStrings : service for tracking page-specific string translations.
+ *
+ * Convience functions embedded herein are prefixed with "$" to avoid
+ * collisions with string keys, which are linked directly to the
+ * service.
+ *
+ * egStrings.A_STRING = 'hello, world {{foo}';
+ *
+ * egStrings.$replace(egStrings.A_STRING, {foo : 'bar'})
+ *
+ */
+
+angular.module('egCoreMod').factory('egStrings',
+['$interpolate', function($interpolate) {
+ return {
+ '$replace' : function(str, args) {
+ if (!str) return '';
+ return $interpolate(str)(args);
+ }
+ }
+}]);