Difference between revisions of "Bitgroup"

From Organic Design wiki
m (Bitgroup messages)
(oops, in its own repo now)
 
(42 intermediate revisions by the same user not shown)
Line 1: Line 1:
These days the issue of privacy online is becoming more and more important as we hear about the shocking extent to which organisations like the NSA are violating people's privacy and the giant corporations like Facebook and Google are misusing our personal information. Social networks have become an important tool for people from diverse locations and cultures to get together and organise, but when this organisation is used for purposes that oppose them they're shut down like [http://worldtruth.tv/government-orders-youtube-to-censor-protest-videos/ this] and [http://arstechnica.com/business/2013/09/facebook-suddenly-deletes-social-fixers-facebook-pages/ this]. It's become very apparent to many people that a new form of social networking tool is required that meets a number of important criteria.
+
{{info|The Bitgroup documentation has now moved to its own wiki at [http://www.bitgroup.org bitgroup.org].}}
 
 
'''Free and open source:''' For it to gain widespread use and be as accessible as possible to all people like Facebook and Google applications are it must be completely free to use. But it's very important that the source code be available for peer review so that the users can be satisfied that it's secure and there's no nasty tricks involved such as misuse of personal information or the inclusion of [http://www.newscientist.com/article/dn24165-how-nsa-weakens-encryption-to-access-internet-traffic.html back doors] through the security systems.
 
 
 
'''Peer-to-peer:''' One of the big problems we're starting to see nowadays is that the centralised model for setting up sites on a web-server for the users to access is not very resilient. We're seeing many sites that start off with great ideals when it comes to upholding peoples rights to privacy, but when they become too popular they come under attack by "the global agenda" by being accused of aiding child pornography or "supporting terrorism". They're then forced by law to turn over their user's data or face being prosecuted and shut down, some such as [http://www.washingtonpost.com/blogs/the-switch/wp/2013/08/09/another-e-mail-service-shuts-down-over-government-spying-concerns/ Silent Circle] and [http://www.washingtonpost.com/blogs/the-switch/wp/2013/08/08/snowdens-e-mail-provider-is-closing-cannot-legally-say-why/ Lavabit] just shut down voluntarily rather than violate their user's privacy or face prosecution. However there are some online applications and services which have come under attack but have not been successfully shut down such as The Pirate Bay and other file sharing applications and the [[Bitcoin]] crypto-currency. Although these applications are considered a major problem from the perspective of the global agenda, it's so far proven impossible to do anything about them. The reason is that they have no central point of attack, shutting down accounts or servers or prosecuting people has no effect on these systems. It's for this reason that it's absolutely necessary for us, the people, to have access to a new social networking tool that is completely [[peer-to-peer]].
 
 
 
'''Supports privacy and anonymity:''' Another thing that has been growing in importance a lot over the last few years is the ability for users to maintain their anonymity when working online. It's rapidly becoming a violation of law simply to voice opinion that opposes the status quo, which is in itself ample reason, or even a moral obligation, to oppose it! It's important for people who oppose this draconian system to be able to do so anonymously, especially in exceedingly oppressive countries that don't allow basic freedom of speech rights such as China and the USA.
 
 
 
== Bitmessage ==
 
Anyone whose thought seriously about developing a any kind of peer-to-peer application that has more than basic file sharing functionality will know that there are some extremely difficult challenges to overcome, particularly when it comes to the "sign up/sign in" and the ability for many users to "subscribe" to information. There are many complex issues involved in security and content distribution which are orders of magnitude more difficult to accomplish in a system where there are no fixed servers, and every peer must be considered as a potentially hostile to the network.
 
 
 
The first application which has really made headway in moving the world of peer-to-peer beyond the simple file-sharing application is the [[Bitcoin]] crypto-currency. The fact that it's financially oriented creates much more motivation for attackers to try and exploit any weakness in it, but it has stood up to everything the hackers and the governments with  their billions of dollars have thrown at it. Now a few years down the track, the Bitcoin code is starting to be used in other more diverse applications such as in the [[Namecoin]] alternative domain name system and the [[Bitmessage]] secure anonymous messaging system.
 
 
 
== Bitgroup ==
 
Bitmessage is of particular interest because messaging is a very general application that can be used as the foundation for many other applications - social networking being one of them. And that's what the Bitgroup project is about, building the foundations of a secure peer-to-peer anonymous social network using Bitmessage as it's fundamental communications over which users and groups are formed and connected, hence the name "Bitgroup".
 
 
 
Bitmessage is programmed in Python, and since I've now had some experience working with the Bitessage API through Python when I made the [[Bitmessage#Bitmessage gateway|Bitmessage gateway]] so the main networking aspect of Bitgroup will also be written in Python. The interface will be in JavaScript running within an embedded browser component within the Python application. This is so that it will be very easy to allow it to be run as a web-application server and will also make the interface programming and theming much simpler.
 
 
 
The code is being developed in our tools repo which you can view on our Github mirror [https://github.com/OrganicDesign/tools/tree/git-svn/Bitgroup here]. It'll get moved into it's own repo after it's our of the pre-alpha stage.
 
 
 
== How groups work in Bitgroup ==
 
The basic functionality required for building a social network application is the [[channel]] which is a collection of changing content maintained by one or more users that can be "subscribed" to, or "followed" by many others. This functionality allows for all the different behaviours that make up a social network system such as creating pages, posting items, following and "liking" things, as well as other potential functionality such as blogs, wikis or scheduling applications.
 
 
 
The first step in this "channels" idea is to extend the basic messaging and subscription functionality of Bitmessage to allow for the formation of groups that have not only subscribers that can view the public content generated by the group, but also "members" who produce and publish the group's content together and also have access to the private members-only content.
 
 
 
There are three key features of Bitmessage that allow for the formation of private groups.
 
*Any number of running instances can have the same ''From'' address, so sharing the private keys associated with an address (or in the case of a deterministically generated address, sharing the key used to create it from) with other people allows them to become a member of the group by being able to publish information from that group to its subscribers or other members.
 
*Users are able to subscribe to their own addresses which means that you will receive broadcast messages from others sending from that same address. This means that a many-to-many messaging system can be set up by a group all using the same address to broadcast from and subscribing to it. They can then use the private key used to create the address as a shared encryption key so that messages broadcast to the group's private address are only readable by members of the group.
 
*Broadcast messages do not reveal the identity of the sender, so the group can be private and the members anonymous even though its possible for a non-member to subscribe to the group's private address if they were to find it somehow (for example by observing the block chain).
 
 
 
When a user creates a new group, the request is processed by the ''newGroup'' method in the main Python ''App'' class (defined in [http://svn.organicdesign.co.nz/filedetails.php?repname=tools&path=%2FBitgroup%2Fapp.py app.py]) which simply instantiates a new ''Group'' instance (the ''Group'' class is defined in the [http://svn.organicdesign.co.nz/filedetails.php?repname=tools&path=%2FBitgroup%2Fgroup.py group.py] module) with the desired name for the new group as the only parameter. When a ''Group'' instance is created with a name instead of an existing Bitmessage address as its construction parameter, it goes through the process of creating a new private key and address pair (public and private) for the group. The ''Group'' constructor then adds an entry for the new group to the "groups" section of the Bitgroup configuration file using the group's private key as the entry key and the group's private Bitmessage address as the entry value. And then finally it creates a new data structure for the group based on a template structure in the ''group.py'' module and adds the new group's name and public Bitmessage address to the structure. The data structure is automatically saved onto disk by the ''Node'' class which is a base-classes of ''Group''. The group's stored data is encrypted using the group's private key.
 
 
 
=== Nodes ===
 
The group functionality created by the ''Group'' class is a messaging-only system with no persistent content, so the next level of functionality is done by a class called ''Node'' (defined in the [http://svn.organicdesign.co.nz/filedetails.php?repname=tools&path=%2FBitgroup%2Fnode.py node.py] module) which this content aspect in the form of a structure of properties and attachments. A group can have any number of nodes which can be viewed and modified in different ways depending on the extensions the group has installed, for example as a folder of files, a list of historical posts, as a collaborative document etc. Later as more extensions are created, this channel functionality can form the basis for many other applications such as group schedules and decision-making tools.
 
 
 
=== Bitgroup messages ===
 
Bitgroup messages are Bitmessage messages that are designed to be interpreted by the Bitgroup application rather than by the recipient user directly. Bitgroup messages are distinguished from normal messages by a simple means of having a subject line that consists of the Bitgroup version that generated the message, the class that it should be instantiated as, and a human readable comment indicating that the message is to be interpreted by Bitgroup. The message body is in the form of a Base64 encoded JSON properties structure. It's up to the specific message class to ensure that the data of the message is valid and from a legitimate source.
 
 
 
Bitgroup messages exist in the Bitmessage network and in user's Bitmessage in-boxes and out-boxes, but at runtime they exist as instances of the ''Message'' class, or more specifically one of the sub-classes of ''Message'' which are all defined in the [http://svn.organicdesign.co.nz/filedetails.php?repname=tools&path=%2FBitgroup%2Fmessage.py message.py] module. The ''Message'' class has methods for sending or broadcasting and other basic Bitmessage operations and also has methods for creating and updating the message's properties, and the sub-classes the refine this common functionality for the classes specific purpose such as data synchronisation or inviting new members.
 
 
 
The ''message.py'' module declares a function called ''getMessageClass'' which takes a Bitmessage message data structure as its single parameter and returns the Bitgroup class that the message should be instantiated as. It does this by checking if the subject line indicates that the message is for Bitgroup to interpret and extracts the class that's specified. It also checks that the class not only exists, but is indeed a sub-class of the ''Message'' class before returning it. If it's not a Bitgroup message, or the class was not a ''Message'' sub-class, then the ''Message'' class is returned which is the Bitgroup wrapper for a plain Bitmessage message.
 
 
 
Since the ''getMessageClass'' function takes a Bitmessage message data structure as its parameter always returns a valid message class, and all ''Message'' classes are also instantiated with a Bitmessage message parameter, a Bitmessage message can be converted to its appropriate Bitgroup class with the following slightly odd looking syntax - in fact I think it's odd enough that I' allowed to add my [[Template:Voodoo|Voodoo icon]] to it ;-)
 
{{code|1=<table><tr>
 
<td>[[File:Voodoo.svg|50px|link=Template:Voodoo]]</td><td>&nbsp;&nbsp;</td>
 
<td><python>msg = getMessageClass(msg)(msg)</python></td>
 
</tr></table>}}
 
 
 
 
 
Bitgroup messages are also present in the interface and so the interface also has a ''Message'' class which is defined in the [http://svn.organicdesign.co.nz/filedetails.php?repname=tools&path=%2FBitgroup%2Finterface%2Fmessage.js message.js] script. The message data is stored in the user's node data so that it's automatically synchronised with the Python side and the back-end storage but is not part of any distributed group data.
 
 
 
Note that only low-level functionality used by the Bitgroup system itself uses these message types. High level workflow for group organisation applications will be implemented later and will be done within the context of group node data which is on a higher level of abstraction than dealing with individual Bitmessages.
 
 
 
=== Invitations ===
 
The first type of Bitmessage implemented is the ''Invitation'' class which is used to invite other Bitgroup users to become a member of a group. If the recipient doesn't yet run Bitgroup (or even Bitmessage), then they'll need to be contacted directly first with a message requesting that they install Bitgroup so that they can then accept an invitation message. Later this process of first inviting users to install Bitgroup will be incorporated into the interface, but initially only the Bitgroup invitation messages are being implemented.
 
 
 
An ''Invitation'' has a property called ''status'' which is set to ''created'' by default when the instance is first created. Once a recipient has been set and the message sent to their address, the message which resides in the local Bitmessage out-box will have a status set to "sent". When the recipient's Bitgroup application receives the message an ''Invitation'' instance will be instantiated for it and its ''status'' will be set to ''received'' and it will at this point have a potential "accept" action associated with it which the Bitgroup interface will present. If the user chooses to accept the invitation, then the ''accept'' method of the ''Invitation'' will be executed and the message's status will be set to a final ''accepted'' value and the process of creating the group's public and private Bitmessage addresses locally will begin.
 
 
 
=== Data synchronisation ===
 
Each group consists of a number of users each running one or more instances of the software (only one instance per host though), and each of these instances may have many interface pages open in their browser. The data for the group needs to be synchronised across all of these instances and interface pages.
 
 
 
The general idea is that each context will queue up its changes and propagate them on a regular interval. The open interface pages connect with the Python service running on their local host on a one-second interval, and each of the services connects with the rest of the online group members on a five second interval and with the offline members via Bitmessages on a half-hourly interval.
 
 
 
As changes to data occur they're queued up to be sent on the next time a synchronisation request occurs. All the data is in the form of a hierarchical key:value structure with every value being a two-element array contain the value and an accompanying timestamp denoting when the current value was set. The timestamp originates at the source of the change and accompanies the value in the queue and then in the sent synchronisation data.
 
 
 
When any instance receives incoming data to be synchronised it checks each items timestamp and compares it with the timestamo if the local version of the same value (if one exists) and only stores it if it's more recent. This way all the instances' data will always eventually end up in a consistent state with each other as long as all the messages eventually arrive, see [[w:Eventual consistency|eventual consistency]].
 
 
 
Each instance has a unique ID so that the service can keep track of the last time each one connected last and merge it's queued client changes with it's local changes that have occurred during that time. I'm using an HTTP header called '''X-Bitgroup-ID''' for assigning an ID to each client because the Python socket implementation doesn't seem to allow one to obtain the unique handle of each connected stream.
 
 
 
== Interface ==
 
Browser-based interfaces are extremely convenient because they allow very easy linking to other sites, and are very easy to develop since there's so many useful tools and technologies available such as [[jQuery]] and CSS, and allows groups to open up some of their content to the web for easy access. Some groups could even run a complete web gateway if privacy and security were less of a concern to them so that their members and subscribers could log in over the web like a normal social networking site. Since the browser will be connecting only to our local socket, and our local socket only accepts requests from our own application the browser is safer than the usual situation. However later we'd like to offer the option of an in-application version of the interface using [http://code.google.com/p/pywebkitgtk/ PyWebkit] or [http://www.pygtk.org/pygtkmozembed/ PyGtkMozembed].
 
 
 
The interface uses the [[Single Page Application]] pattern for the structure of the interface code. This keeps the roles of the Python and the JavaScript very clear with no need for a messy mish-mash of interface functionality spanning both the sides. The entire interface application will be written in JavaScript and only reloads the page/application when the user changes the context to a different group (since each group decides for themselves which extensions will be active and therefore which JavaScript will be loaded in the page).
 
 
 
The local part of the URL consists only of the group name or Bitmessage address and everything after that is the hash-fragment, so that only a change in group will result in a page-reload. The URL format after the hash is a slash-separated path, the first element being the currently selected ''view'' and subsequent elements being decoded by that view's URL-routing method and sent to the relevant method for processing.
 
 
 
To allow for more rapid interface development and make it easier for other developers to be involved and extend the system we're including [http://jquery.com/ jQuery] and using [http://jqueryui.com/ jQueryUI] for all the interface elements such as tooltips, progress bars, tab-sets etc.
 
 
 
The Python side of the application is purely involved with Group communications across the network and with keeping the group's property structures up to date. Any new messages or changes to properties on either side are kept in sync with the other side via a per-second polling request from the client side. Both sides queue any changes to data and on the next request, the server merges the queues, updates its data and then responds with a final list of items changed for the client to update its data with.
 
 
 
=== Application start-up sequence ===
 
First the Python-based Bitgroup service needs to be run before the interface can be loaded since the interface code and resources are served to the browser from this service. After the interface is running in the browser it can be used even if the Python service is stopped as long as the group is not changed. Any data changes made while the background service isn't running will be queued up and sent after it can be connected to again. A change in group requires a page reload (and therefore needs the service to be present) since only one group's data and extensions are loaded at once in case one group you're a member of chooses to run extensions that another group you're a member of doesn't trust.
 
 
 
When the browser first requests a page from Bitmessage's HTTP server (defined in [http://svn.organicdesign.co.nz/filedetails.php?repname=tools&path=%2FBitgroup%2Fhttp.py http.py]), it returns a page containing just an empty body element and a head element containing the main application script, the default views (currently "overview" and "newgroup") and some common resources such jQuery and jQueryUI. The page also contains a small number of variables specifying the current group, user information and the group's extension names. Everything then starts with the execution of the main application script, [http://svn.organicdesign.co.nz/filedetails.php?repname=tools&path=%2FBitgroup%2Finterface%2Fmain.js main.js].
 
 
 
The ''main.js'' script defines a class called ''App'' which encapsulates all the main functionality of the entire application, and then instantiates a ''singleton'' instance of this class called ''window.app''. Execution is then handed over to the constructor of the ''App'' class.
 
 
 
The constructor first copies all the data that was sent in the request HTML head element into the application instance, and then does an Ajax request to retrieve ''i18n.json'' which contains all the internationalisation messages used by the application (this file is also loaded into the Python side so that both sides can share the same system messages). After the ''i18n'' messages have finished loading, the group's extensions are loaded and a handler is registered to respond to changes in the "hash fragment" portion of the URL (see [[Single Page Application]] for more details about what this means). Execution is then passed to the application's main start-up method, ''run()''.
 
 
 
The ''run()'' method first calls the ''locationChange()'' method to simulate the changing of the hash-fragment in the URL so that the current view can be rendered into the content area of the page. If there is no group selected, then the ''pageRender'' method is called since it will not be automatically rendered in response to data events in this case. The final step in the start-up process is to set up an event that runs every second which keeps the data synchronised between the backend and client side.
 
 
 
From then on, everything that happens in the ongoing running of the application is in response to either user interface events or data-change events coming from the Python service.
 
 
 
=== Page rendering sequence ===
 
In a pure Single Page Application page rendering is a rare occurrence because there are no page reloads. In Bitgroup a page render occurs when the user changes the group as described above since it causes a page reload. A full page render is also done if data synchronisation hasn't occurred for some time or the number of changes received is over a certain amount.
 
 
 
The first response from the server side for a ''dataSync'' request returns the entire data structure (this happens if the last sync was more than a certain time ago or the number of changes is more than a certain amount). The ''dataSync'' method detects if the response is a list of changes or the whole structure, and if it's the whole structure it calls ''renderPage'' which builds the entire page from scratch.
 
 
 
The ''renderPage'' method first loads the CSS for the group's selected skin, or the default if no skin is selected. It then renders the personal links  in the top bar and the views menu, and then renders empty ''div'' elements for the main page items such as notification. page title and content areas which will be filled later. The skin's JavaScript (if it has any) is then executed which may completely alter the layout of the page as long as it keeps the elements ID attributes intact.
 
 
 
After the skin's script as finished executing the ''renderPage'' method will then finish off the page by populating the title area, connecting the live menu elements to their data sources and executing the selected page view method, or ''overview'' if none is selected. The view method is responsible for populating the main content area of the page and is called not only in a full page render, but also whenever a new view is selected from the view menu or when the currently selected node changes.
 
 
 
=== Forms & data ===
 
In a Single Page Application, the input elements that allow the user to update data and content in the application do not use the classic form/post methodology because everything is handled by the currently loaded page. In fact the entire ''form'' element is quite redundant and so is the ''name'' attribute of input elements. Instead the form components simply connect into the application functionality directly via event handlers.
 
 
 
In the Bitgroup application we have some methods of the main ''App'' class that make the handling of inputs much simpler. First we have ''componentType'' which returns a general type string when passed an input element. This type string is used to determine how the value of the input is set or retrieved and how changes in the value are to be connected in to the application. The ''componentRender'' method is a high-level helper function for constructing an input element given some data such as a list, and ''componentSetValue / componentGetValue'' are used for setting or retrieving the value of a form or other interface component.
 
 
 
Components can have their own ''getValue'' and/or ''setValue'' functions by adding them directly to the element and then these will be used by ''componentSetValue'' and ''componentGetValue'' if they're present. This way it's easy to make complicated custom components that are connected to live data changes.
 
 
 
'''App.componentConnect:''' The main function is ''componentConnect'' which creates a "live" connection between the specified path into the group's data and the interface component. The connection is bidirectional (if it's an input component) and asynchronous so that if the user changes the value it will automatically propagate back to the server-side to be stored and will automatically be reflected in any other open instances of the interface. Conversely if the data undergoes change from another remote instance via another user in the group or by some other back-end service, all interface components connected to the changed data will automatically have their values updated.
 
 
 
Here's a simple example of how it can be used:
 
{{code|<js>
 
$('#foo').html( app.componentRender( 'checklist', ['foo', 'bar', 'baz'] ) );
 
app.componentConnect( 'settings.extensions', $('#foo .checklist') );
 
</js>}}
 
In this example, a checklist (like a select list but with checkboxes instead) is rendered with items "foo", "bar" and "baz". The second line then connects this new input element to the data source ''settings.extensions'' in the group's shared data structure. As soon as this is done, any of the checklist items that are in the ''settings.extensions'' value will become checked, and if any of the items checked state are changed by the user, the group's data and any other pages containing the input will update accordingly.
 
 
 
Here's a slightly more complicated example which creates a live table connected to a data source that's an array. Whenever the data in the array changes, the element's custom setter method is called which renders the data as a table into the element.
 
{{code|<js>
 
var inbox = document.getElementById( 'inbox' );
 
inbox.setValue = function( val ) {
 
var rows = '<tr><th>From</th><th>Subject</th></tr>';
 
for( var i in val ) rows += '<tr><td>' + val[i].from + '</td><td>' + val[i].subject + '</td></tr>';
 
$(this).html( '<table>' + rows + '</table>' );
 
};
 
app.componentConnect( 'data.messages', inbox );
 
</js>}}
 
 
 
 
 
One complication (bug#2) is that the event handler prevents the DOM element that it's connected to from dying when it's no longer present in the DOM, so the event handler first checks this by using a ''jQuery'' statement to check if the component is a descendant of the ''body'' element, and removes itself from the event if not.
 
{{code|<js>
 
var handler = function( event ) {
 
if( $(element).parents().filter('body').length > 0 ) event.args.app.componentSetValue( element, event.args.val );
 
else $(document).off( event.type, null, handler );
 
};
 
$(document).on( "bgDataChange-" + key, handler );
 
</js>}}
 
The handler function is assigned to a variable in the local scope so that it can be referred to when it's called to unhook itself from the event using the [http://api.jquery.com/off/ jQuery .off() method].
 
 
 
== Skins ==
 
The main content is generated unconditionally by the main application and is a plain set of ''div'' and ''ul'' elements with CSS class and ID attributes allowing CSS to define the style and page layout. Skins are a directory of the skins name that go in the ''skins'' directory and contain at least a ''style.css'' stylesheet that will be loaded if the group has selected that skin. There can then be any number of support files such as images etc in the skin's directory that the stylesheet can refer to.
 
 
 
== Extensions ==
 
Extensions play an important role in the Bitgroup system. The idea is that each group's members can choose what extensions are active for them based on their needs and their security concerns. The extensions are only implemented in the JavaScript side currently as that's where the majority of the application's functionality resides - the Python side is only the foundation network functionality which is unlikely to require extending and raises security issues at least until the application has reached a mature state of development. Extensions are in the form of directories that reside in the ''extensions'' directory. The directory should be the name of the extension, and it should contain a JavaScript file of the same name within it that has the ''.js'' file extension. This file will be loaded prior to application startup if the current group has selected to use it. If the user changes to a different group, the page reloads so that a new selection of extension can load depending on the group's preferences.
 
 
 
=== Adding new views ===
 
To add a new view, an extension defines a class, adds a render method which populates the ''content'' div, and adds the class to the available views in the ''app.views'' list. It may also add the view name to the various node types that should have the view.
 
{{code|<js>
 
function MyView() {
 
// do any initialisation of the view here such as loading dependencies or subscribing to events etc
 
}
 
 
 
// Populate the content div element
 
MyView.prototype.render = function(app) {
 
$('#content').html( 'Hello World!' );
 
};
 
 
 
// Create a singleton instance of our new view in the app's available views list
 
window.app.views.push( new MyView() );
 
</js>}}
 
 
 
=== Application events ===
 
We need to add custom events throughout the code that extensions can subscribe to so they can adjust or extend the behaviour of the application. Extensions will initially only be available on the JavaScript side and will use the jQuery [http://api.jquery.com/trigger/ trigger] method. Events are triggered with parameters passed to the handler that are specific to the event are packaged inside an ''args'' object so that they can be adjusted since JavaScript doesn't support passing-by-reference on non-object variables.
 
 
 
This is an example of an event the could be triggered by an extension:
 
{{code|<js>
 
var args = { foo: 'bar' };
 
$.event.trigger({ type: "bgNewEvent", args: args });
 
alert( args.foo );
 
</js>}}
 
Here the argument ''foo'' is being output after the new ''bgNewEvent'' event is triggered to see if any handlers have changed it's value. We precede the event name with the "bg" prefix to ensure there's no conflict with existing events in the environment.
 
 
 
A handler could subscribe to this event and modify the ''foo'' value as follows:
 
{{code|<js>
 
$(document).on( "bgNewEvent", function( event ) {
 
event.args.foo = 'baz';
 
});
 
</js>}}
 
 
 
==== Current events ====
 
'''bgHashChange'''
 
 
 
'''bgPoller'''
 
 
 
'''bgDataChange-KEY'''
 
  
 
== See also ==
 
== See also ==
*Bitgroup source code: [http://svn.organicdesign.co.nz/listing.php?repname=tools&path=%2FBitgroup%2F&#afea2590aba19e4fb2f3dd76e758577c0 WebSVN] | [https://github.com/OrganicDesign/tools/tree/git-svn/Bitgroup GitHub mirror]
+
*Bitgroup source code: [http://svn.organicdesign.co.nz/listing.php?repname=bitgroup WebSVN] | [https://github.com/OrganicDesign/bitgroup GitHub mirror]
 
*[[Bitgroup/Bugs]] ''- a record of the bugs and fixes''
 
*[[Bitgroup/Bugs]] ''- a record of the bugs and fixes''
 
*[[Bitgroup/Tasks]] ''- a record of the tasks to do and done''
 
*[[Bitgroup/Tasks]] ''- a record of the tasks to do and done''
 
*[[Bitmessage]]
 
*[[Bitmessage]]
 
*[[Bitcoin]]
 
*[[Bitcoin]]
 +
*[[Python]]
 
*[[Single Page Application]]
 
*[[Single Page Application]]
 
*[[Closure]]
 
*[[Closure]]
  
[[Category:Projects]]
+
[[Category:Projects]][[Category:Bitgroup]]

Latest revision as of 16:23, 23 October 2013

Info.svg The Bitgroup documentation has now moved to its own wiki at bitgroup.org.


See also