diff --git a/src/objects/models.py b/src/objects/models.py index 409c6aad0f..7ad13c661b 100644 --- a/src/objects/models.py +++ b/src/objects/models.py @@ -144,9 +144,9 @@ class ObjectDB(TypedObject): # make sure to sync the contents cache when initializing #_GA(self, "contents_update")() - def _at_db_player_presave(self): + def _at_db_player_postsave(self): """ - This hook is called automatically just before the player field is saved. + This hook is called automatically after the player field is saved. """ # we need to re-cache this for superusers to bypass. self.locks.cache_lock_bypass(self) diff --git a/src/server/caches.py b/src/server/caches.py index 3405b0aa53..c55d50df3d 100644 --- a/src/server/caches.py +++ b/src/server/caches.py @@ -80,28 +80,28 @@ def hashid(obj, suffix=""): #------------------------------------------------------------ # callback to field pre_save signal (connected in src.server.server) -def field_pre_save(sender, instance=None, update_fields=None, raw=False, **kwargs): - """ - Called at the beginning of the field save operation. The save method - must be called with the update_fields keyword in order to be most efficient. - This method should NOT save; rather it is the save() that triggers this - function. Its main purpose is to allow to plug-in a save handler and oob - handlers. - """ - if raw: - return - if update_fields: - # this is a list of strings at this point. We want field objects - update_fields = (_GA(_GA(instance, "_meta"), "get_field_by_name")(field)[0] for field in update_fields) - else: - # meta.fields are already field objects; get them all - update_fields = _GA(_GA(instance, "_meta"), "fields") - for field in update_fields: - fieldname = field.name - handlername = "_at_%s_presave" % fieldname - handler = _GA(instance, handlername) if handlername in _GA(sender, '__dict__') else None - if callable(handler): - handler() +#def field_pre_save(sender, instance=None, update_fields=None, raw=False, **kwargs): +# """ +# Called at the beginning of the field save operation. The save method +# must be called with the update_fields keyword in order to be most efficient. +# This method should NOT save; rather it is the save() that triggers this +# function. Its main purpose is to allow to plug-in a save handler and oob +# handlers. +# """ +# if raw: +# return +# if update_fields: +# # this is a list of strings at this point. We want field objects +# update_fields = (_GA(_GA(instance, "_meta"), "get_field_by_name")(field)[0] for field in update_fields) +# else: +# # meta.fields are already field objects; get them all +# update_fields = _GA(_GA(instance, "_meta"), "fields") +# for field in update_fields: +# fieldname = field.name +# handlername = "_at_%s_presave" % fieldname +# handler = _GA(instance, handlername) if handlername in _GA(sender, '__dict__') else None +# if callable(handler): +# handler() def field_post_save(sender, instance=None, update_fields=None, raw=False, **kwargs): diff --git a/src/server/oob_cmds.py b/src/server/oob_cmds.py index 88236104e0..42a762c3bf 100644 --- a/src/server/oob_cmds.py +++ b/src/server/oob_cmds.py @@ -18,14 +18,19 @@ instance and session is the active session to get return data. The function names are not case-sensitive (this allows for names like "LIST" which would otherwise collide with Python builtins). -A function named _OOB_ERROR will retrieve error strings if it is +A function named OOB_ERROR will retrieve error strings if it is defined. It will get the error message as its 3rd argument. + +Data is usually returned via + session.msg(oob=(cmdname, (args,), {kwargs})) +Note that args, kwargs must be iterable/dict, non-iterables will +be interpreted as a new command name. + """ from django.conf import settings _GA = object.__getattribute__ _SA = object.__setattr__ -_NA_REPORT = lambda o: (None, "N/A") _NA_SEND = lambda o: "N/A" #------------------------------------------------------------ @@ -33,26 +38,25 @@ _NA_SEND = lambda o: "N/A" # cmdname(oobhandler, session, *args, **kwargs) #------------------------------------------------------------ -def _OOB_ERROR(oobhandler, session, errmsg, *args, **kwargs): +def OOB_ERROR(oobhandler, session, errmsg, *args, **kwargs): """ A function with this name is special and is called by the oobhandler when an error occurs already at the execution stage (such as the oob function not being recognized or having the wrong args etc). """ - session.msg(oob=("send", {"ERROR": errmsg})) + session.msg(oob=("err", ("ERROR " + errmsg,))) def ECHO(oobhandler, session, *args, **kwargs): "Test/debug function, simply returning the args and kwargs" session.msg(oob=("echo", args, kwargs)) - +##OOB{"SEND":"CHARACTER_NAME"} def SEND(oobhandler, session, *args, **kwargs): """ This function directly returns the value of the given variable to the session. """ - print "In SEND:", oobhandler, session, args obj = session.get_puppet_or_player() ret = {} if obj: @@ -62,14 +66,18 @@ def SEND(oobhandler, session, *args, **kwargs): ret[name] = value except Exception, e: ret[name] = str(e) - # return result - session.msg(oob=("send", ret)) - + session.msg(oob=("send", ret)) + else: + session.msg(oob=("err", ("You must log in first.",))) +##OOB{"REPORT":"TEST"} def REPORT(oobhandler, session, *args, **kwargs): """ This creates a tracker instance to track the data given in *args. + The tracker will return with a oob structure + oob={"report":["attrfieldname", (args,), {kwargs}} + Note that the data name is assumed to be a field is it starts with db_* and an Attribute otherwise. @@ -79,27 +87,37 @@ def REPORT(oobhandler, session, *args, **kwargs): obj = session.get_puppet_or_player() if obj: for name in (a.upper() for a in args if a): - typ, val = OOB_REPORTABLE.get(name, _NA_REPORT)(obj) - if typ == "field": - oobhandler.track_field(obj, session.sessid, name) - elif typ == "attribute": - oobhandler.track_attribute(obj, session.sessid, name) + trackname = OOB_REPORTABLE.get(name, None) + if not trackname: + session.msg(oob=("err", ("No Reportable property '%s'. Use LIST REPORTABLE_VARIABLES." % trackname,))) + elif trackname.startswith("db_"): + oobhandler.track_field(obj, session.sessid, trackname) + else: + oobhandler.track_attribute(obj, session.sessid, trackname) + else: + session.msg(oob=("err", ("You must log in first.",))) -def UNREPORT(oobhandler, session, vartype="prop", *args, **kwargs): +##OOB{"UNREPORT": "TEST"} +def UNREPORT(oobhandler, session, *args, **kwargs): """ This removes tracking for the given data given in *args. """ obj = session.get_puppet_or_player() if obj: for name in (a.upper() for a in args if a): - typ, val = OOB_REPORTABLE.get(name, _NA_REPORT) - if typ == "field": - oobhandler.untrack_field(obj, session.sessid, name) + trackname = OOB_REPORTABLE.get(name, None) + if not trackname: + session.msg(oob=("err", ("No Un-Reportable property '%s'. Use LIST REPORTED_VALUES." % name,))) + elif trackname.startswith("db_"): + oobhandler.untrack_field(obj, session.sessid, trackname) else: # assume attribute - oobhandler.untrack_attribute(obj, session.sessid, name) + oobhandler.untrack_attribute(obj, session.sessid, trackname) + else: + session.msg(oob=("err", ("You must log in first.",))) +##OOB{"LIST":"COMMANDS"} def LIST(oobhandler, session, mode, *args, **kwargs): """ List available properties. Mode is the type of information @@ -135,15 +153,42 @@ def LIST(oobhandler, session, mode, *args, **kwargs): session.msg(oob=("list", ("REPORTABLE_VARIABLES",) + tuple(key for key in OOB_REPORTABLE.keys()))) elif mode == "REPORTED_VARIABLES": - session.msg(oob=("list", ("REPORTED_VARIABLES",) + - tuple(oobhandler.get_all_tracked(session)))) + # we need to check so as to use the right return value depending on if it is + # an Attribute (identified by tracking the db_value field) or a normal database field + reported = oobhandler.get_all_tracked(session) + reported = [stored[2] if stored[2] != "db_value" else stored[4][0] for stored in reported] + session.msg(oob=("list", ["REPORTED_VARIABLES"] + reported)) elif mode == "SENDABLE_VARIABLES": session.msg(oob=("list", ("SENDABLE_VARIABLES",) + tuple(key for key in OOB_REPORTABLE.keys()))) - #elif mode == "CONFIGURABLE_VARIABLES": - # pass + elif mode == "CONFIGURABLE_VARIABLES": + # Not implemented (game specific) + pass else: - session.msg(oob=("list", ("unsupported mode",))) + session.msg(oob=("err", ("LIST", "Unsupported mode",))) + +def _repeat_callback(oobhandler, session, *args, **kwargs): + "Set up by REPEAT" + session.msg(oob=("repeat", ("Repeat!",))) + +##OOB{"REPEAT":10} +def REPEAT(oobhandler, session, interval, *args, **kwargs): + """ + Test command for the repeat functionality. Note that the args/kwargs + must not be db objects (or anything else non-picklable), rather use + dbrefs if so needed. The callback must be defined globally and + will be called as + callback(oobhandler, session, *args, **kwargs) + """ + oobhandler.repeat(None, session.sessid, interval, _repeat_callback, *args, **kwargs) + + +##OOB{"UNREPEAT":10} +def UNREPEAT(oobhandler, session, interval): + """ + Disable repeating callback + """ + oobhandler.unrepeat(None, session.sessid, interval) # Mapping for how to retrieve each property name. @@ -167,9 +212,10 @@ OOB_SENDABLE = { "UTF_8": lambda o: True } -# mapping for which properties may be tracked. Each callable should return a tuple (type, value) where -# the type is one of "field" or "attribute" depending on what is being tracked. +# mapping for which properties may be tracked. Each value points either to a database field +# (starting with db_*) or an Attribute name. OOB_REPORTABLE = { - "CHARACTER_NAME": lambda o: ("field", o.key), - "ROOM_NAME": lambda o: ("attribute", o.db_location.key) + "CHARACTER_NAME": "db_key", + "ROOM_NAME": "db_location", + "TEST" : "test" } diff --git a/src/server/oobhandler.py b/src/server/oobhandler.py index aeacfe3c04..1dc4aca882 100644 --- a/src/server/oobhandler.py +++ b/src/server/oobhandler.py @@ -36,7 +36,6 @@ messages. from inspect import isfunction from twisted.internet.defer import inlineCallbacks -from twisted.internet.task import LoopingCall from django.conf import settings from src.server.models import ServerConfig from src.server.sessionhandler import SESSIONS @@ -57,12 +56,12 @@ for mod in make_iter(settings.OOB_PLUGIN_MODULES): _OOB_FUNCS.update(dict((key.lower(), func) for key, func in all_from_module(mod).items() if isfunction(func))) # get custom error method or use the default -_OOB_ERROR = _OOB_FUNCS.get("_OOB_ERROR", None) +_OOB_ERROR = _OOB_FUNCS.get("oob_error", None) if not _OOB_ERROR: # create default oob error message function def oob_error(oobhandler, session, errmsg, *args, **kwargs): "Error wrapper" - session.msg(oob=("send", {"ERROR": errmsg})) + session.msg(oob=("err", ("ERROR ", errmsg))) _OOB_ERROR = oob_error @@ -105,16 +104,16 @@ class TrackerHandler(object): def remove(self, fieldname, trackerclass, *args, **kwargs): """ - Remove tracker from handler. Raises KeyError if tracker - is not found. + Remove identified tracker from TrackerHandler. + Raises KeyError if tracker is not found. """ trackerkey = trackerclass.__name__ tracker = self.tracktargets[fieldname][trackerkey] try: - tracker.at_delete(*args, **kwargs) + tracker.at_remove(*args, **kwargs) except Exception: logger.log_trace() - del tracker + del self.tracktargets[fieldname][trackerkey] self.ntrackers -= 1 if self.ntrackers <= 0: # if there are no more trackers, clean this handler @@ -173,9 +172,9 @@ class ReportFieldTracker(TrackerBase): new_value = new_value.key except AttributeError: new_value = to_str(new_value, force_string=True) + kwargs[self.fieldname] = new_value # this is a wrapper call for sending oob data back to session - self.oobhandler.msg(self.sessid, "report", self.fieldname, - new_value, *args, **kwargs) + self.oobhandler.msg(self.sessid, "report", *args, **kwargs) class ReportAttributeTracker(TrackerBase): @@ -199,8 +198,9 @@ class ReportAttributeTracker(TrackerBase): new_value = new_value.dbobj except AttributeError: new_value = to_str(new_value, force_string=True) + kwargs[self.attrname] = new_value # this is a wrapper call for sending oob data back to session - self.oobhandler.msg(self.sessid, "report", self.attrname, new_value, *args, **kwargs) + self.oobhandler.msg(self.sessid, "report", *args, **kwargs) @@ -208,25 +208,21 @@ class ReportAttributeTracker(TrackerBase): class OOBTicker(Ticker): """ - Version of Ticker that calls OOB_FUNC rather than trying to call + Version of Ticker that executes an executable rather than trying to call a hook method. """ @inlineCallbacks - def _callback(self, oobhandler, sessions): + def _callback(self): "See original for more info" for key, (_, args, kwargs) in self.subscriptions.items(): - session = sessions.session_from_sessid(kwargs.get("sessid")) + # args = (sessid, callback_function) + session = SESSIONS.session_from_sessid(args[0]) try: - oobhandler.execute_cmd(session, kwargs.get("func_key"), *args, **kwargs) + # execute the oob callback + yield args[1](OOB_HANDLER, session, *args[2:], **kwargs) except Exception: logger.log_trace() - def __init__(self, interval): - "Sets up the Ticker" - self.interval = interval - self.subscriptions = {} - self.task = LoopingCall(self._callback, OOB_HANDLER, SESSIONS) - class OOBTickerPool(TickerPool): ticker_class = OOBTicker @@ -270,9 +266,9 @@ class OOBHandler(object): tracker_storage = ServerConfig.objects.conf(key="oob_tracker_storage") if tracker_storage: self.oob_tracker_storage = dbunserialize(tracker_storage) - #print "recovered from tracker_storage:", self.oob_tracker_storage for (obj, sessid, fieldname, trackerclass, args, kwargs) in self.oob_tracker_storage.values(): - self.track(unpack_dbobj(obj), sessid, fieldname, trackerclass, *args, **kwargs) + #print "restoring tracking:",obj, sessid, fieldname, trackerclass + self._track(unpack_dbobj(obj), sessid, fieldname, trackerclass, *args, **kwargs) # make sure to purge the storage ServerConfig.objects.conf(key="oob_tracker_storage", delete=True) self.tickerhandler.restore() @@ -302,6 +298,7 @@ class OOBHandler(object): storekey = (obj_packed, sessid, propname) stored = (obj_packed, sessid, propname, trackerclass, args, kwargs) self.oob_tracker_storage[storekey] = stored + #print "_track:", obj, id(obj), obj.__dict__ def _untrack(self, obj, sessid, propname, trackerclass, *args, **kwargs): """ @@ -312,9 +309,8 @@ class OOBHandler(object): obj = obj.dbobj except AttributeError: pass - try: - # call at_delete hook + # call at_remove hook on the trackerclass _GA(obj, "_trackerhandler").remove(propname, trackerclass, *args, **kwargs) except AttributeError: pass @@ -327,7 +323,7 @@ class OOBHandler(object): Get the names of all variables this session is tracking. """ sessid = session.sessid - return [key[2].lstrip("db_") for key in self.oob_tracker_storage.keys() if key[1] == sessid] + return [stored for key, stored in self.oob_tracker_storage.items() if key[1] == sessid] def track_field(self, obj, sessid, field_name, trackerclass=ReportFieldTracker): """ @@ -336,14 +332,14 @@ class OOBHandler(object): """ # all database field names starts with db_* field_name = field_name if field_name.startswith("db_") else "db_%s" % field_name - self._track(obj, sessid, field_name, trackerclass) + self._track(obj, sessid, field_name, trackerclass, field_name) - def untrack_field(self, obj, sessid, field_name): + def untrack_field(self, obj, sessid, field_name, trackerclass=ReportFieldTracker): """ Shortcut for untracking a database field. Uses OOBTracker by defualt """ field_name = field_name if field_name.startswith("db_") else "db_%s" % field_name - self._untrack(obj, sessid, field_name) + self._untrack(obj, sessid, field_name, trackerclass) def track_attribute(self, obj, sessid, attr_name, trackerclass=ReportAttributeTracker): """ @@ -353,14 +349,15 @@ class OOBHandler(object): """ # get the attribute object if we can try: - obj = obj.dbobj + attrobj = obj.dbobj except AttributeError: pass - attrobj = _GA(obj, "attributes").get(attr_name, return_obj=True) + attrobj = obj.attributes.get(attr_name, return_obj=True) + #print "track_attribute attrobj:", attrobj, id(attrobj) if attrobj: self._track(attrobj, sessid, "db_value", trackerclass, attr_name) - def untrack_attribute(self, obj, sessid, attr_name, trackerclass): + def untrack_attribute(self, obj, sessid, attr_name, trackerclass=ReportAttributeTracker): """ Shortcut for deactivating tracking for a given attribute. """ @@ -368,25 +365,24 @@ class OOBHandler(object): obj = obj.dbobj except AttributeError: pass - attrobj = _GA(obj, "attributes").get(attr_name, return_obj=True) + attrobj = obj.attributes.get(attr_name, return_obj=True) if attrobj: - self._untrack(attrobj, sessid, attr_name, trackerclass) + self._untrack(attrobj, sessid, "db_value", trackerclass, attr_name) - def repeat(self, obj, sessid, func_key, interval=20, *args, **kwargs): + def repeat(self, obj, sessid, interval=20, callback=None, *args, **kwargs): """ - Start a repeating action. Every interval seconds, - the oobfunc corresponding to func_key is called with - args and kwargs. + Start a repeating action. Every interval seconds, trigger + callback(*args, **kwargs). The callback is called with + args and kwargs; note that *args and **kwargs may not contain + anything un-picklable (use dbrefs if wanting to use objects). """ - if not func_key in _OOB_FUNCS: - raise KeyError("%s is not a valid OOB function name.") - self.tickerhandler.add(self, obj, interval, func_key=func_key, sessid=sessid, *args, **kwargs) + self.tickerhandler.add(obj, interval, sessid, callback, *args, **kwargs) - def unrepeat(self, obj, sessid, func_key, interval=20): + def unrepeat(self, obj, sessid, interval=20): """ Stop a repeating action """ - self.tickerhandler.remove(self, obj, interval) + self.tickerhandler.remove(obj, interval) # access method - called from session.msg() @@ -396,23 +392,26 @@ class OOBHandler(object): Retrieve oobfunc from OOB_FUNCS and execute it immediately using *args and **kwargs """ - try: - #print "OOB execute_cmd:", session, func_key, args, kwargs, _OOB_FUNCS.keys() - oobfunc = _OOB_FUNCS[func_key] # raise traceback if not found - oobfunc(self, session, *args, **kwargs) - except KeyError,e: - errmsg = "OOB Error: function '%s' not recognized: %s" % (func_key, e) + oobfunc = _OOB_FUNCS.get(func_key, None) + if not oobfunc: + # function not found + errmsg = "OOB Error: function '%s' not recognized." % func_key if _OOB_ERROR: _OOB_ERROR(self, session, errmsg, *args, **kwargs) + logger.log_trace() else: logger.log_trace(errmsg) - raise KeyError(errmsg) + return + + # execute the found function + try: + #print "OOB execute_cmd:", session, func_key, args, kwargs, _OOB_FUNCS.keys() + oobfunc(self, session, *args, **kwargs) except Exception, err: errmsg = "OOB Error: Exception in '%s'(%s, %s):\n%s" % (func_key, args, kwargs, err) if _OOB_ERROR: _OOB_ERROR(self, session, errmsg, *args, **kwargs) - else: - logger.log_trace(errmsg) + logger.log_trace(errmsg) raise Exception(errmsg) def msg(self, sessid, funcname, *args, **kwargs): diff --git a/src/server/portal/websocket_client.py b/src/server/portal/websocket_client.py index 2fd8c158f9..d3a7bd553a 100644 --- a/src/server/portal/websocket_client.py +++ b/src/server/portal/websocket_client.py @@ -31,7 +31,7 @@ import json from twisted.internet.protocol import Protocol from src.server.session import Session from src.utils.logger import log_trace -from src.utils.utils import to_str +from src.utils.utils import to_str, make_iter from src.utils.text2html import parse_html @@ -85,7 +85,8 @@ class WebSocketClient(Protocol, Session): try: oobdata = json.loads(string) for (key, args) in oobdata.items(): - self.data_in(text=None, oob=(key, args)) + #print "oob data in:", (key, args) + self.data_in(text=None, oob=(key, make_iter(args))) except Exception: log_trace("Websocket malformed OOB request: %s" % string) else: @@ -119,6 +120,7 @@ class WebSocketClient(Protocol, Session): self.sendLine(str(e)) if "oob" in kwargs: oobstruct = self.sessionhandler.oobstruct_parser(kwargs.pop("oob")) + #print "oob data_out:", "OOB" + json.dumps(oobstruct) self.sendLine("OOB" + json.dumps(oobstruct)) raw = kwargs.get("raw", False) nomarkup = kwargs.get("nomarkup", False) diff --git a/src/server/server.py b/src/server/server.py index 9a0c7f1ee5..bc0f8c3e3a 100644 --- a/src/server/server.py +++ b/src/server/server.py @@ -33,9 +33,9 @@ from src.server.sessionhandler import SESSIONS # setting up server-side field cache from django.db.models.signals import post_save -from src.server.caches import field_pre_save +from src.server.caches import field_post_save #pre_save.connect(field_pre_save, dispatch_uid="fieldcache") -post_save.connect(field_pre_save, dispatch_uid="fieldcache") +post_save.connect(field_post_save, dispatch_uid="fieldcache") #from src.server.caches import post_attr_update #from django.db.models.signals import m2m_changed diff --git a/src/server/serversession.py b/src/server/serversession.py index ce04bfd0fa..00551f4b42 100644 --- a/src/server/serversession.py +++ b/src/server/serversession.py @@ -206,6 +206,7 @@ class ServerSession(Session): if not _OOB_HANDLER: from src.server.oobhandler import OOB_HANDLER as _OOB_HANDLER oobstruct = self.sessionhandler.oobstruct_parser(kwargs.pop("oob", None)) + #print "session.data_in: oobstruct:",oobstruct for (funcname, args, kwargs) in oobstruct: if funcname: _OOB_HANDLER.execute_cmd(self, funcname, *args, **kwargs) diff --git a/src/server/sessionhandler.py b/src/server/sessionhandler.py index b911226095..2dc3edd152 100644 --- a/src/server/sessionhandler.py +++ b/src/server/sessionhandler.py @@ -136,14 +136,17 @@ class SessionHandler(object): elif isinstance(oobstruct[1], (tuple, list)): # cmdname, (args,) return (oobstruct[0].lower(), list(oobstruct[1]), {}) + else: + # cmdname, cmdname + return ((oobstruct[0].lower(), (), {}), (oobstruct[1].lower(), (), {})) else: # cmdname, (args,), {kwargs} return (oobstruct[0].lower(), list(oobstruct[1]), dict(oobstruct[2])) if hasattr(oobstruct, "__iter__"): # differentiate between (cmdname, cmdname), - # (cmdname, args, kwargs) and ((cmdname,args,kwargs), - # (cmdname,args,kwargs), ...) + # (cmdname, (args), {kwargs}) and ((cmdname,(args),{kwargs}), + # (cmdname,(args),{kwargs}), ...) if oobstruct and isinstance(oobstruct[0], basestring): return (list(_parse(oobstruct)),) diff --git a/src/typeclasses/models.py b/src/typeclasses/models.py index c4b03a743d..5abea472e9 100644 --- a/src/typeclasses/models.py +++ b/src/typeclasses/models.py @@ -70,7 +70,7 @@ _DA = object.__delattr__ #------------------------------------------------------------ #class Attribute(SharedMemoryModel): -class Attribute(WeakSharedMemoryModel): +class Attribute(SharedMemoryModel): """ Abstract django model. @@ -173,13 +173,6 @@ class Attribute(WeakSharedMemoryModel): """ self.db_value = to_pickle(new_value) self.save(update_fields=["db_value"]) - try: - # eventual OOB hook - #self._track_db_value_change.update(self.cached_value) - self._track_db_value_change.update(self.new_value) - except AttributeError: - pass - return #@value.deleter def __value_del(self): diff --git a/src/web/media/javascript/evennia_websocket_webclient.js b/src/web/media/javascript/evennia_websocket_webclient.js index b604fc92fc..e913f65e5b 100644 --- a/src/web/media/javascript/evennia_websocket_webclient.js +++ b/src/web/media/javascript/evennia_websocket_webclient.js @@ -20,13 +20,38 @@ var OOB_debug = true // Custom OOB functions // functions defined here can be called by name by the server. For -// example the OOB{"echo":arguments} will trigger a function named -// echo(arguments). +// example the OOB{"echo":(args),{kwargs}} will trigger a function named +// echo(args, kwargs). -function echo(message) { +function echo(args, kwargs) { // example echo function. - doShow("out", "ECHO return: " + message) } + doShow("out", "ECHO return: " + args) } + +function list (args, kwargs) { + // show in main window + doShow("out", args) } + +function send (args, kwargs) { + // show in main window. SEND returns kwargs {name:value}. + for (sendvalue in kwargs) { + doShow("out", sendvalue + " = " + kwargs[sendvalue]);} +} + +function report (args, kwargs) { + // show in main window. REPORT returns kwargs + // {attrfieldname:value} + for (name in kwargs) { + doShow("out", name + " = " + kwargs[name]) } +} + +function repeat (args, kwargs) { + // called by repeating oob funcs + doShow("out", args) } + +function err (args, kwargs) { + // display error + doShow("err", args) } // Webclient code @@ -61,15 +86,20 @@ function onMessage(evt) { var inmsg = evt.data if (inmsg.length > 3 && inmsg.substr(0, 3) == "OOB") { // dynamically call oob methods, if available - try {var oobarray = JSON.parse(inmsg.slice(3));} // everything after OOB } + try { + var oobarray = JSON.parse(inmsg.slice(3));} // everything after OOB } catch(err) { // not JSON packed - a normal text - doShow('out', err + " " + inmsg); + doShow('out', inmsg); return; } - for (var ind in oobarray) { - try { window[oobarray[ind][0]](oobarray[ind][1]) } - catch(err) { doShow("err", "Could not execute OOB function " + oobtuple[0] + "(" + oobtuple[1] + ")!") } + if (typeof oobarray != "undefined") { + for (var ind in oobarray) { + try { + window[oobarray[ind][0]](oobarray[ind][1], oobarray[ind][2]) } + catch(err) { + doShow("err", "Could not execute js OOB function '" + oobarray[ind][0] + "(" + oobarray[ind][1] + oobarray[ind][2] + ")'") } + } } } else { @@ -93,8 +123,15 @@ function doSend(){ if (OOB_debug && outmsg.length > 4 && outmsg.substr(0, 5) == "##OOB") { // test OOB messaging - doShow("out", "OOB input: " + outmsg.slice(5)) - doOOB(JSON.parse(outmsg.slice(5))); } + try { + doShow("out", "OOB input: " + outmsg.slice(5)); + if (outmsg.length == 5) { + doShow("err", "OOB testing syntax: ##OOB{\"cmdname:args, ...}"); } + else { + doOOB(JSON.parse(outmsg.slice(5))); } } + catch(err) { + doShow("err", err) } + } else { // normal output websocket.send(outmsg); }