service.pending = [];
service.socket = null;
service.hatchAvailable = null;
- service.hatchURL = 'wss://localhost:8443/hatch';
+ service.defaultHatchURL = 'wss://localhost:8443/hatch';
service.hatchRequired = false;
- // TODO: would be nice to support local fall-through for specific actions
// 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 '" + msg.action + "' command to Hatch");
+
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.dispatchRequest = function(msg) {
+ service.attemptHatchDelivery = function(msg) {
msg.msgid = service.msgId++;
msg.deferred = $q.defer();
service.messages[msg.msgid] = msg;
- if (service.hatchAvailable === false) {
- // Hatch is known to be closed
- service.pending.push(msg);
- service.handleRequestsLocally();
+ if (service.hatchAvailable === false) { // Hatch is closed
+ msg.deferred.reject(msg);
+ //service.pending.push(msg);
+ //service.handleRequestsLocally();
- } else if (service.hatchAvailable === true) {
+ } else if (service.hatchAvailable === true) { // Hatch is open
// Hatch is known to be open
service.sendToHatch(msg);
- } else {
- // Hatch status unknown; attempt to connect
+ } 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
// resolve / reject
if (msg.error) {
- console.error("egPrintStore command failed : "
+ throw new Error(
+ "egPrintStore command failed : "
+ JSON.stringify(msg.error, null, 2));
- msg.deferred.reject(msg.error);
} else {
msg.deferred.resolve(msg.content);
}
);
}
- switch(msg.action) {
- case 'print':
- service.browserPrint(msg);
- break;
- case 'keys':
- case 'get':
- case 'set':
- case 'append':
- case 'remove':
- service.handleLocalStorageRequest(msg);
- break;
- case 'printers' :
- // there is fall-through handler for printers
- msg.content = [];
- break;
- default:
- console.debug(
- 'no fall-thru handler for requested action: ' + msg.action);
+ 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.hatchConnect = function() {
if (service.socket &&
console.debug("connecting to Hatch...");
try {
- service.socket = new WebSocket(service.hatchURL);
+ var url = service.getLocalItem('eg.hatch.url')
+ || service.defaultHatchURL;
+ service.socket = new WebSocket(url);
} catch(e) {
- service.handleRequestsLocally();
+ service.hatchAvailable = false;
+ service.hatchClosed();
return;
}
service.socket.onclose = function() {
if (service.hatchAvailable === false) return; // already registered
- console.debug("disconnected from Hatch");
- service.socket = null;
- service.hatchAvailable = null; // reset
- if (service.onHatchClose)
- service.onHatchClose();
+
+ // 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.handleRequestsLocally();
- if (service.onHatchClose)
- service.onHatchClose();
+ service.hatchClosed();
}
service.socket.onmessage = function(evt) {
var msgStr = evt.data;
-
- if (!msgStr) {
- console.error("Hatch returned empty message");
- return;
- }
+ if (!msgStr) throw new Error("Hatch returned empty message");
var msgObj = JSON.parse(msgStr);
console.debug('Hatch says ' + JSON.stringify(msgObj, null, 2));
}
}
- // print locally via the browser
- service.browserPrint = function(msg) {
- service.onBrowserPrint(msg.contentType, msg.content);
- }
-
- /**
- * TODO: local and hatch templates need to go through generation..
- * */
-
service.interpolateHtmlTemplate = function(template, printScope) {
// TODO: for print template security, we must scrub
// the scope object and remove any references to
}
- // print the provided content
// 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.getPrintConfig()
- .then(function(conf) {
- return service.dispatchRequest({
- key : 'no-op',
- action : 'print',
- config : conf[context],
- content : content,
- contentType : contentType,
- showDialog : withDialog
- });
- });
+ 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.getItem('eg.printing.config')
+ return service.getRemoteItem('eg.printing.config')
.then(function(conf) {
return (service.printConfig = conf || {})
});
}
-
service.setPrintConfig = function(conf) {
service.printConfig = conf;
- return service.setItem('eg.printing.config', 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.
// dispatch the print configuration request
.then(function(config) {
+
+ // loaded remote config
if (!config[context]) config[context] = {};
config[context].printer = printer;
- return service.dispatchRequest({
+ return service.attemptHatchDelivery({
key : 'no-op',
action : 'print-config',
config : config[context]
}
service.getPrinters = function() {
- if (service.printers)
+ if (service.printers) // cached printers
return $q.when(service.printers);
- return service.dispatchRequest({key : 'no-op', action : 'printers'})
- .then(function(printers) {
- service.printers = printers.sort(
- function(a,b) {return a.name < b.name ? -1 : 1});
- return 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.dispatchRequest({key : key, action : 'get'});
+ 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);
}
- // set the value for a stored or new item
service.setItem = function(key, value) {
- value = js2JSON(value); // all values stored as JSON text
- return service.dispatchRequest(
- {key : key, value : value, action : 'set'});
+ return service.setRemoteItem(key, value)['catch'](
+ function(msg) {
+ return service.setLocalItem(msg.key, msg.value);
+ }
+ );
+ }
+
+ // set the value for a stored or new item
+ service.setRemoteItem = function(key, value) {
+ value = JSON.stringify(value); // all values stored as JSON text
+ return service.attemptHatchDelivery({
+ key : key,
+ value : value,
+ action : 'set',
+ });
+ }
+
+ service.setLocalItem = function(key, value) {
+ $window.localStorage.setItem(key, JSON.stringify(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.dispatchRequest(
- {key : key, value : value, action : 'append'});
+ 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
+ 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.dispatchRequest({key : key, action : 'remove'});
+ 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.dispatchRequest({key : prefix, action : 'keys'});
+ return service.getRemoteKeys(prefix)['catch'](
+ function() {
+ return service.getLocalKeys(prefix)
+ }
+ );
}
- // get, set, remove, append, keys : via $window.localStorage
- service.handleLocalStorageRequest = function(msg) {
- console.log('service.handleLocalStorageRequest() ' + msg.action);
- var key = msg.key;
- var value = msg.value;
- switch(msg.action) {
- case 'keys':
- var keys = [];
- var idx = 0;
- while ( (k = $window.localStorage.key(idx++)) !== null) {
- // key prefix match test
- if (key && k.substr(0, key.length) != key) continue;
- keys.push(k);
- }
- msg.content = keys;
- break;
-
- case 'set':
- $window.localStorage.setItem(key, value);
- break;
-
- case 'get':
- msg.content = $window.localStorage.getItem(msg.key);
- break;
-
- case 'remove':
- $window.localStorage.removeItem(msg.key);
- break;
-
- case 'append':
- var item = $window.localStorage.getItem(key);
- if (item) value = item + value;
- $window.localStorage.setItem(key, value);
- msg.content = value;
- break;
+ 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;