text: /*!
   * ${copyright}
   */
  
  /**
   * XMPP JSONPatch Sync Model
   * 
   * This model is based on the newest research in middleware and distributed systems.
   * It is using internally two CRDT compliant add-only sets to guarantee eventually
   * consistency.
   * https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type
   * 
   * In easy words:
   * This model can synchronize with other models in real time, can be used offline
   * and won't produce any conflicts when reconnected to the globale state.
   * 
   * It connects via websocket to an XMPP server (Strophe) and creates or subscribses to a
   * node. To this node all changes to the model are send as JSONPath add operation.
   * 
   * https://xmpp.org/extensions/xep-0060.html
   * 
   * Other models connected to the same node can receive the messages and apply them 
   * to their own internal state so in the end all connected clients will be consistent.
   * 
   * @namespace
   * @name incentergy.model
   * @public
   */
  // Provides the XMPPJSONPatchSyncModel object based model implementation
  sap.ui.define(['jquery.sap.global', 'sap/ui/model/ClientModel', 'sap/ui/model/Context', './XMPPJSONPatchSyncListBinding', './XMPPJSONPatchSyncPropertyBinding', './hash/XXH', 'strophe'],
      function(jQuery, ClientModel, Context, XMPPJSONPatchSyncListBinding, XMPPJSONPatchSyncPropertyBinding, XXH) {
          "use strict";
          var Strophe = window.Strophe;
          var XMPPJSONPatchSyncModel = ClientModel.extend("incentergy.model.XMPPJSONPatchSyncModel", /** @lends incentergy.model.XMPPJSONPatchSyncModel.prototype */ {
  
              /**
               * Creates a new XMPPJSONPatchSyncModel without connecting yet.
               * 
               * @param {URL} xmppServerUrl the URL of the XMPP Server websocket or bosh endpoint e.g. "https://patchcord.be/websocket/"
               * @param {String} node  the node to uss
               * @param {String} jid optional a jid to use
               * @param {any} password optional a password to use
               * @param {any} debug extends the Strophe log function with a console log
               */
              constructor: function(xmppServerUrl, node, jid, password, debug) {
                  ClientModel.apply(this, arguments);
  
                  var me = this;
                  if (debug) {
                      Strophe.log = function(level, logMessage) {
                          console.log(level + " " + logMessage);
                      };
                  }
  
                  // Inserts and Removes are grow only sets.
                  // This means that operations like delete or replace
                  // will create inconsistent states.
                  // only adding of objects when they are not there
                  // yet it allowed 
                  // The key of the sets is generated by using the xxHash
                  // algorithm on the JSON representation of the object.
                  // Cycles and functions are forbidden in this data structure.
                  this._oData = {
                      inserts: {},
                      removes: {}
                  };
                  this.sNode = node;
                  this.sXmppServerUrl = xmppServerUrl;
                  this.sJid = jid;
                  this.sPassword = password;
  
                  this.oOwnerDeterminedPromise = new Promise(function(resolve, reject) {
                      me.oOwnerDeterminedResolved = resolve;
                      me.oOwnerDeterminedReject = reject;
                  });
  
                  this.oOwnerDeterminedPromise.catch(function(e) {
                      // log error 
                      console.log(e);
                  });
  
                  // Set max size a lot higher than normal
                  this.setSizeLimit(1048576);
              },
  
              metadata: {}
          });
  
          XMPPJSONPatchSyncModel.NS_PUBSUB = "http://jabber.org/protocol/pubsub";
          XMPPJSONPatchSyncModel.NS_PUBSUB_OWNER = "http://jabber.org/protocol/pubsub#owner";
          XMPPJSONPatchSyncModel.NS_DATA_FORMS = "jabber:x:data";
          XMPPJSONPatchSyncModel.NS_DISCO_INFO = "http://jabber.org/protocol/disco#info";
  
          /**
           * Connects to the given XMPP Server and creates the node if it is not available yet
           * otherwise it will subscribe to the node.
  
           */
          XMPPJSONPatchSyncModel.prototype.connect = function() {
              var me = this;
  
              if (!this.sJid) {
                  this.sJid = this._getOrGenerateJid(this.sXmppServerUrl);
              }
  
              if (!this.sPassword) {
                  this.sPassword = "";
              }
              var oConnection = new Strophe.Connection(
                  this.sXmppServerUrl, {
                      protocol: this.sXmppServerUrl.match(/^(.*)?:/)[1],
                      mechanisms: [
                          Strophe.SASLAnonymous, Strophe.SASLPlain, Strophe.SASLMD5, Strophe.SASLSHA1
                      ]
                  });
  
              // TODO: Check what we need here
              this.sPubSubService = "pubsub." + this._extractServerDomainFromUrl(this.sXmppServerUrl);
  
              oConnection.connect(this.sJid, this.sPassword, function(status) {
                  if (status === Strophe.Status.CONNECTED) {
                      me.fireEvent("connectionStatus", { status: "CONNECTED", server: me.sXmppServerUrl });
                      me.createIfNecessaryAndSubscribeToTopic(me.sNode);
                  } else if (status === Strophe.Status.DISCONNECTED) {
                      me.fireEvent("connectionStatus", { status: "DISCONNECTED", server: me.sXmppServerUrl });
                  }
              });
  
              this.connection = oConnection;
          };
  
          XMPPJSONPatchSyncModel.prototype.ownerDetermined = function() {
              return this.oOwnerDeterminedPromise;
          };
  
          /**
           * Creates if necessary a node and subscribses to the node. This Model will
           * use the messages from the node to update the internal data.
           */
          XMPPJSONPatchSyncModel.prototype.createIfNecessaryAndSubscribeToTopic = function(node) {
              var me = this;
              // first, set up a callback for the events
              this.connection.addHandler(
                  function() {
                      return me.onMessage.apply(me, arguments);
                  },
                  null, "message", null, null, this.sPubSubService);
  
              // check if we already have a subscription ()
              var checkSubcriptions = $iq({
                      to: this.sPubSubService,
                      from: this.connection.jid,
                      type: "get"
                  })
                  .c('pubsub', { xmlns: XMPPJSONPatchSyncModel.NS_PUBSUB })
                  .c('subscriptions', {
                      node: node
                  });
              this.connection.sendIQ(checkSubcriptions,
                  function(oMessage) {
                      var subscriptions = oMessage.getElementsByTagName("subscription");
                      // Success
                      if (subscriptions.length == 0) {
                          // if there are no elements in the subscription
                          var createiq = $iq({
                                  to: me.sPubSubService,
                                  type: "set"
                              })
                              .c('pubsub', { xmlns: XMPPJSONPatchSyncModel.NS_PUBSUB })
                              .c('create', { node: node }).up()
                              .c('configure').c('x', { xmlns: XMPPJSONPatchSyncModel.NS_DATA_FORMS, type: 'submit' })
                              .c('field', {
                                  var: 'FORM_TYPE',
                                  type: 'hidden'
                              }).c('value', {}, 'http://jabber.org/protocol/pubsub#node_config').up()
                              .c('field', { var: 'pubsub#publish_model' }).c('value', {}, 'open');
  
                          // try to create the node
                          me.connection.sendIQ(createiq,
                              // created
                              function() {
                                  me.fireEvent("connectionStatus", { status: "NODE_CREATED", node: node, server: me.sXmppServerUrl });
                                  me.subscribeToNode(node);
                                  me.queryAffiliationForNode(node, function() { return me._setOwner.apply(me, arguments); });
                              },
                              // error, the node is probably already there
                              function() {
                                  me.fireEvent("connectionStatus", { status: "NODE_CREATED_ERROR", node: node, server: me.sXmppServerUrl });
                                  // So just subscribe to the given node
                                  // <iq xmlns='jabber:client' from='pubsub.localhost' to='e3bd845b-a9c8-9a8f-21f9-756844ee857a@localhost/8950023574515464431173674181627361664567003529430652952392' type='error' id='a9918840-ba45-4d2e-a57a-1684538788e4:sendIQ'><pubsub xmlns='http://jabber.org/protocol/pubsub'><create node='7e5863eb-e23b-5489-4f32-222372b5836b'/></pubsub><error code='409' type='cancel'><conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error></iq>
                                  me.subscribeToNode(node);
                                  me.queryAffiliationForNode(node, function() { return me._setOwner.apply(me, arguments); });
                              });
                      } else {
                          me.fireEvent("connectionStatus", { status: "SUBSCRIPTION_ALREADY_IN_PLACE", node: node, server: me.sXmppServerUrl });
                          me.queryAffiliationForNode(node, function() { return me._setOwner.apply(me, arguments); });
                          me.requestAllItems(node, function() {
                              me.unsubscribeFromNode(node, subscriptions[0].getAttribute("jid"), function() {
                                  me.subscribeToNode(node);
                              });
                          });
  
                      }
                  },
                  function(e) {
                      // Error
                      jQuery.sap.log.warning("Error! " + e);
                  });
  
              // });
          };
  
          /**
           * Checks if this model is the owner of the node.
           * @param {any} oMessage affiliation response message
           * @param {string} sNode the name of the node
           */
          XMPPJSONPatchSyncModel.prototype._setOwner = function(oMessage, sNode) {
              var affiliations = oMessage.getElementsByTagName("affiliation");
              if (affiliations.length > 0) {
                  if (affiliations[0].getAttribute("affiliation") == "owner") {
                      this.oOwnerDeterminedResolved();
                      return;
                  }
              }
              this.oOwnerDeterminedReject();
          };
  
          /**
           * Subscribe to given node
           * @param {string} the node to subscribe
           */
          XMPPJSONPatchSyncModel.prototype.subscribeToNode = function(node) {
              var me = this;
              // now subscribe
              var subiq = $iq({
                      to: this.sPubSubService,
                      type: "set"
                  })
                  .c('pubsub', { xmlns: XMPPJSONPatchSyncModel.NS_PUBSUB })
                  .c('subscribe', {
                      node: node,
                      jid: this.connection.jid
                  });
              this.connection.sendIQ(subiq,
                  function() {
                      me.fireEvent("connectionStatus", { status: "NODE_SUBSCRIBED", node: node, server: me.sXmppServerUrl });
                      me.requestAllItems(node);
                  },
                  function() {
                      me.fireEvent("connectionStatus", { status: "NODE_SUBCRIBED_ERROR", node: node, server: me.sXmppServerUrl });
                  });
          };
  
          /**
           * Unsubscribe the jid from the given node and call the success callback in the vent of success
           * @param {any} node the node to unsubscribe from
           * @param {any} jid the bare jid of the current user without resource
           * @param {any} success 
           */
          XMPPJSONPatchSyncModel.prototype.unsubscribeFromNode = function(node, jid, success) {
              var me = this;
              // now subscribe
              var unsubiq = $iq({
                      to: this.sPubSubService,
                      from: this.connection.jid,
                      type: "set"
                  })
                  .c('pubsub', { xmlns: XMPPJSONPatchSyncModel.NS_PUBSUB })
                  .c('unsubscribe', {
                      node: node,
                      jid: jid
                  });
              this.connection.sendIQ(unsubiq,
                  function(oMessage) {
                      me.fireEvent("connectionStatus", { status: "NODE_UNSUBSCRIBED", node: node, server: me.sXmppServerUrl });
                      if (typeof success === "function") {
                          success(oMessage, node);
                      }
                  },
                  function() {
                      me.fireEvent("connectionStatus", { status: "NODE_UNSUBCRIBED_ERROR", node: node, server: me.sXmppServerUrl });
                  });
          };
  
          /**
           * Request all items that were already send to the node.
           * @param {any} node
           */
          XMPPJSONPatchSyncModel.prototype.requestAllItems = function(node, success) {
              var me = this;
              var items = $iq({
                      to: this.sPubSubService,
                      type: "get"
                  })
                  .c('pubsub', { xmlns: XMPPJSONPatchSyncModel.NS_PUBSUB })
                  .c('items', {
                      node: node
                  });
              this.connection.sendIQ(items,
                  function(oMessage) {
                      me.fireEvent("connectionStatus", { status: "NODE_ITEMS_REQUESTED", node: node, server: me.sXmppServerUrl });
                      var values = oMessage.getElementsByTagName("value");
                      for (var i = 0; i < values.length; i++) {
                          me.processItem(values[i].textContent);
                      }
                      me.checkUpdate();
                      if (typeof success == "function") {
                          success(oMessage, node);
                      }
                  },
                  function() {
                      me.fireEvent("connectionStatus", { status: "NODE_ITEMS_REQUESTED_ERROR", node: node, server: me.sXmppServerUrl });
                  });
          };
  
          /**
           * Delete the given node
           */
          XMPPJSONPatchSyncModel.prototype.deleteNode = function(node, success) {
              var me = this;
              // now delete
              var deleteiq = $iq({
                      to: this.sPubSubService,
                      from: this.connection.jid,
                      type: "set"
                  })
                  .c('pubsub', { xmlns: XMPPJSONPatchSyncModel.NS_PUBSUB_OWNER })
                  .c('delete', {
                      node: node
                  });
              this.connection.sendIQ(deleteiq,
                  function(oMessage) {
                      me.fireEvent("connectionStatus", { status: "NODE_DELETED", node: node, server: me.sXmppServerUrl });
                      if (typeof success === "function") {
                          success(oMessage, node);
                      }
                  },
                  function() {
                      me.fireEvent("connectionStatus", { status: "NODE_DELETED_ERROR", node: node, server: me.sXmppServerUrl });
                  });
          };
  
          /**
           * Delete the node on the XMPP server and clear the model
           * afterwards refresh the bindings.
           */
          XMPPJSONPatchSyncModel.prototype.clear = function() {
              var me = this;
              this.ownerDetermined().then(function() {
                  me._publishToXmppTopic("replace", "/", { inserts: {}, removes: {} });
                  me.deleteNode(me.sNode, function() {
                      me.createIfNecessaryAndSubscribeToTopic(me.sNode);
                  });
                  me._oData = {
                      inserts: {},
                      removes: {}
                  };
                  me.checkUpdate();
              });
          };
  
          /**
           * Query the given node for information
           * @param {string} node the node to query
           */
          XMPPJSONPatchSyncModel.prototype.queryNode = function(node) {
              var me = this;
              // now delete
              var disco = $iq({
                      to: this.sPubSubService,
                      from: this.connection.jid,
                      type: "get"
                  })
                  .c('query', {
                      xmlns: XMPPJSONPatchSyncModel.NS_DISCO_INFO,
                      node: node
                  });
              this.connection.sendIQ(disco,
                  function() {
                      me.fireEvent("connectionStatus", { status: "NODE_QUERY_RESPONSE", node: node, server: me.sXmppServerUrl });
                  },
                  function() {
                      me.fireEvent("connectionStatus", { status: "NODE_QUERY_ERROR", node: node, server: me.sXmppServerUrl });
                  });
          };
  
          /**
           * Get the affiliation to find the owner.
           * @param {string} node the node to query
           */
          XMPPJSONPatchSyncModel.prototype.queryAffiliationForNode = function(node, callback) {
              var me = this;
              // now delete
              var affiliations = $iq({
                      to: this.sPubSubService,
                      from: this.connection.jid,
                      type: "get"
                  })
                  .c('pubsub', { xmlns: XMPPJSONPatchSyncModel.NS_PUBSUB })
                  .c('affiliations', {
                      node: node
                  });
              this.connection.sendIQ(affiliations,
                  function(oMessage) {
                      if (typeof callback === "function") {
                          callback(oMessage, node);
                      }
                      me.fireEvent("connectionStatus", { status: "AFFILIATION_RESPONSE", node: node, server: me.sXmppServerUrl });
                  },
                  function() {
                      me.fireEvent("connectionStatus", { status: "AFFILIATION_ERROR", node: node, server: me.sXmppServerUrl });
                  });
          };
  
          /**
           * Handler that is called when a message from the XMPP server arrives.
           * @param {any} oMessage the messages that was retrieved from XMPP
           */
          XMPPJSONPatchSyncModel.prototype.onMessage = function(oMessage) {
              var values = oMessage.getElementsByTagName("value");
              if (values.length === 0) {
                  return true;
              }
              if (values.length == 1) {
                  var itemText = values[0].textContent;
                  this.processItem(itemText);
              } else {
                  jQuery.sap.log.warning("Expected length of values to be 1 but is: " + values.length);
              }
              this.checkUpdate();
  
              this.fireEvent("message", { message: oMessage, server: this.sXmppServerUrl });
              return true;
          };
  
          /**
           * Process the item that we received from the node e.g.
           * 
           * {"op": add", "path":"/inserts/62afb01a", "value":{"created":"2017-03-19T10:41:48.823Z","title":"Hello World"}}
           * 
           * http://jsonpatch.com/
           * 
           * @param {string} itemText The change represented as JSONPatch object
           */
          XMPPJSONPatchSyncModel.prototype.processItem = function(itemText) {
              var change = JSON.parse(itemText);
              if (change.path.match(/\/inserts\/(.*)/)) {
                  this._oData.inserts[RegExp.$1] = change.value;
              } else if (change.path.match(/\/removes\/(.*)/)) {
                  this._oData.removes[RegExp.$1] = change.value;
              } else if (change.op === "replace" && change.path == "/") {
                  // Somebody restarted everything. So let's restart aswell
                  window.location.reload();
              } else {
                  jQuery.sap.log.warning("Expected path to start with inserts or removes but is: " + change.path);
              }
          };
  
          /**
           * This function will receive the JID from the local storage or otherwise generate a JID
           * and save it to the local storage.
           * 
           * https://xmpp.org/extensions/xep-0029.html
           */
          XMPPJSONPatchSyncModel.prototype._getOrGenerateJid = function(xmppServerUrl) {
              var jid = window.localStorage.getItem("jid");
              if (!jid) {
                  var sServerDomain = this._extractServerDomainFromUrl(xmppServerUrl);
                  jid = this.guid() + "@" + sServerDomain;
                  window.localStorage.setItem("jid", jid);
              }
              return jid;
          };
  
          /**
           * Extract the domain from the Server URL
           */
          XMPPJSONPatchSyncModel.prototype._extractServerDomainFromUrl = function(sXmppServerUrl) {
              return sXmppServerUrl.match(/.*?:\/\/([^\/:]*)/)[1];
          };
  
          /**
           * Abstract implementation needed by ClientModel.
           */
          XMPPJSONPatchSyncModel.prototype.loadData = function() {
  
          };
  
          /**
           * Generates a random GUID.
           * http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
           * This does not follow the official rules by:
           * https://www.ietf.org/rfc/rfc4122.txt
           * 
           * Nevertheless should be good enough for us.
           */
          XMPPJSONPatchSyncModel.prototype.guid = function() {
              function s4() {
                  return Math.floor((1 + Math.random()) * 0x10000)
                      .toString(16)
                      .substring(1);
              }
              return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
                  s4() + '-' + s4() + s4() + s4();
          };
  
  
          /**
           * Adds an Item to the insert set. It will be added by
           * first calculating the xxHash and afterwards adding the object.
           * 
           * @param {any} item the object to add
           * @param {boolean} bSuppressCheckUpdate do not call checkUpdate
           */
          XMPPJSONPatchSyncModel.prototype.addItem = function(item, bSuppressCheckUpdate) {
              var jsonItem = JSON.stringify(item);
              var objectKey = XXH.h32(jsonItem, 0xABCD).toString(16);
              this._oData.inserts[objectKey] = item;
              this._publishAddToXmppTopic("/inserts/" + objectKey, item);
              if (!bSuppressCheckUpdate) {
                  this.checkUpdate();
              }
          };
  
          /**
           * Adds an Item to the remove set. It will be added by
           * first calculating the xxHash and afterwards adding the object.
           * 
           * @param {any} item the object to add
           * @param {boolean} bSuppressCheckUpdate do not call checkUpdate
           */
          XMPPJSONPatchSyncModel.prototype.removeItem = function(item, bSuppressCheckUpdate) {
              var jsonItem = JSON.stringify(item);
              var objectKey = XXH.h32(jsonItem, 0xABCD).toString(16);
              this._oData.removes[objectKey] = item;
              this._publishAddToXmppTopic("/removes/" + objectKey, item);
              if (!bSuppressCheckUpdate) {
                  this.checkUpdate();
              }
          };
  
          /**
           * Adds an item to the internal storage and sends the item
           * to the node.
           * 
           * @param {any} item The item to send 
           * @param {any} objectKey The object key
           */
          XMPPJSONPatchSyncModel.prototype._publishAddToXmppTopic = function(path, item) {
              this._publishToXmppTopic("add", path, item);
          };
  
          /**
           * Publishes the three parameters as JSON patch to the internally saved
           * sNode.
           * @param {string} op
           * @param {string} path
           * @param {string} item
           */
          XMPPJSONPatchSyncModel.prototype._publishToXmppTopic = function(op, path, item) {
              this.connection.sendIQ(
                  $iq({ to: this.sPubSubService, type: "set" })
                  .c('pubsub', { xmlns: XMPPJSONPatchSyncModel.NS_PUBSUB })
                  .c('publish', { node: this.sNode })
                  .c('item')
                  .c('x', {
                      xmlns: XMPPJSONPatchSyncModel.NS_DATA_FORMS,
                      type: "result"
                  })
                  .c('field', { "var": "change" })
                  .c('value').t(JSON.stringify({
                      "op": op,
                      "path": path,
                      "value": item
                  })).up().up());
          };
  
          /**
           * Returns the value for the property with the given <code>sPropertyName</code>
           *
           * @param {string} sPath the path to the property
           * @param {object} [oContext=null] the context which will be used to retrieve the property
           * @type any
           * @return the value of the property
           * @public
           */
          XMPPJSONPatchSyncModel.prototype.getProperty = function(sPath, oContext) {
              return this._getObject(sPath, oContext);
          };
  
          /**
           * setProperty in Model
           */
          XMPPJSONPatchSyncModel.prototype.setProperty = function(sPath, value, oContext) {
              // TODO: Not implemented yet
              // we have to add the current object to the remove sets
              // afterwards we have to add the new object to the inserts set
              var objectToUpdate;
              if (oContext) {
                  objectToUpdate = this._getObject(oContext.getPath());
              }
  
              var aParts = sPath.split("/"),
                  iIndex = 0;
              if (!aParts[0]) {
                  // absolute path starting with slash
                  iIndex++;
              }
              // Get the last element as the path
              sPath = aParts.pop();
              while (objectToUpdate && aParts[iIndex]) {
                  objectToUpdate = objectToUpdate[aParts[iIndex]];
                  iIndex++;
              }
  
              this.removeItem(objectToUpdate, true);
              var newObject = $.extend({}, objectToUpdate);
              newObject[sPath] = value;
              this.addItem(newObject, true);
              this.checkUpdate(false);
              return false;
          };
  
          /**
           * @param {string} sPath
           * @param {object} [oContext]
           * @returns {any} the node of the specified path/context
           */
          XMPPJSONPatchSyncModel.prototype._getObject = function(sPath, oContext) {
  
              if (sPath !== undefined && !sPath.match("^/items") && oContext !== undefined && !oContext.getPath().match("/items")) {
                  jQuery.sap.log.warning("This model only supports an items array");
                  return undefined;
              }
              jQuery.sap.measure.start(this.getId() + "---_getObject", "_getObject of " + sPath, ["XMPPJsonPatchSyncModel"]);
  
              var me = this;
  
              // oData 
              var oNode = { "items": [] };
              Object.keys(this._oData.inserts) /*.sort()*/ .map(function(key, index) {
                  if (!(key in me._oData.removes)) {
                      oNode.items.push(me._oData.inserts[key]);
                  }
              });
  
              if (oContext instanceof Context) {
                  oNode = this._getObject(oContext.getPath());
              } else if (oContext) {
                  oNode = oContext;
              }
              if (!sPath) {
                  jQuery.sap.measure.end(this.getId() + "---_getObject");
                  return oNode;
              }
              var aParts = sPath.split("/"),
                  iIndex = 0;
              if (!aParts[0]) {
                  // absolute path starting with slash
                  iIndex++;
              }
              while (oNode && aParts[iIndex]) {
                  oNode = oNode[aParts[iIndex]];
                  iIndex++;
              }
              jQuery.sap.measure.end(this.getId() + "---_getObject");
              return oNode;
          };
  
  
          /**
           * @see sap.ui.model.Model.prototype.bindList
           *
           */
          XMPPJSONPatchSyncModel.prototype.bindList = function(sPath, oContext, aSorters, aFilters, mParameters) {
              var oBinding = new XMPPJSONPatchSyncListBinding(this, sPath, oContext, aSorters, aFilters, mParameters);
              return oBinding;
          };
          /**
           * @see sap.ui.model.Model.prototype.bindProperty
           *
           */
          XMPPJSONPatchSyncModel.prototype.bindProperty = function(sPath, oContext, mParameters) {
              var oBinding = new XMPPJSONPatchSyncPropertyBinding(this, sPath, oContext, mParameters);
              return oBinding;
          };
  
          return XMPPJSONPatchSyncModel;
      });