GithubHelp home page GithubHelp logo

geodan / cow Goto Github PK

View Code? Open in Web Editor NEW
8.0 20.0 8.0 15.31 MB

Concurrent Online Workspace

License: Other

JavaScript 87.63% Shell 0.50% HTML 11.00% ApacheConf 0.02% CSS 0.03% PLpgSQL 0.82%

cow's Introduction

Concurrent Online Workspace

COW is a real time multi-user data sharing library for the browser. It uses (secure) websockets to send changes to its peers and indexeddb to store features in the browser for offline usage.

Currently the server is a normal websocket node.js server behind a haproxy which handles the secure bit.

COW works on the following browsers:

  • OSX Safari 7
  • Chrome 31 +
  • Firefox 26 +
  • Internet Explorer (IE) 10 +

Setting up a new cow:

//Create core object
var core = new Cow.core({herdname: 'test'});

//add a default socketserver
cow.socketservers({
 _id: 'default', 
 data: {protocol:'ws',ip:'192.168.25.152', port:8081}
});
cow.socketserver('default');

var connection;
cow.connect().then(function(d){
        connection = d;
}, function(e){
    console.log('Connecting error', e);
});

From here on you are set to work with the workspace according to the API.

API

COW is a workspace to concurrently share data with peers over a webscoket. Peers represent the clients who connected to the same websocket. It is build around a core object that binds together the syncStores, records and messaging components.

Schematically, it looks like:

  • core
    • websocket
    • socketserverStore (1)
      • socketservers (2)
    • peerStore (1)
      • peers (2)
    • userStore (1)
      • users (2)
    • projectStore (1)
      • projects (2)
        • groupStore (1)
          • groups (2)
        • itemStore (1)
          • items (2)

(1) = Store (2) = Record


All the stores share the same basemethods as follows (userStore as example):

    core.users({}) -> adds an empty record, returns record object
    core.users(<string>) -> returns record object with id = <string>
    core.users([<string>]) -> returns array of record objects with matching ids
    core.users()   -> returns array of all record objects
    core.users(<timestamp>) -> returns array of records created before timestamp
    core.userStore() -> returns the userstore object
    core.userStore().syncRecords() -> syncs all records with dirty == true

When adding a new record, it is possible to include data in the object like: cow.users({_id: 1, data: {name: 'myname'}}). The _id parameter is optional. If you don't give it, a new id will be automatically assigned to the record by COW. We recommend to let COW assign the id for you to avoid the risk of having doublings.

Some store's are configured differently:

  • peerStore doesn't use local storage (indexeddb) since peers are unique in every session
  • stores can have a maximum lifetime for the records. When a record isn't updated for x time then the record is not used anymore. It is still kept in the localstorage however.
  • only the projectstore and itemstore keep a history of delta's. Other stores have disabled that option for the sake of data reduction

All record objects behave the same* and as follows (item object as example):

    item.id() -> returns the id of the record
    item.created() -> returns the timestamp of creation
    item.dirty() -> returns the dirtystatus of the record
    item.dirty(boolean) -> sets the dirtystatus of the record, returns record
    item.deleted() -> returns a boolean (true, false) indicating wether the record has been deleted
    item.deleted(boolean) -> sets the record to deleted, returns record
    item.deleted(<timestamp>) -> returns the deleted status at timestamp
    item.updated() -> returns the timestamp (last edit) of the record
    item.updated(<timestamp>) -> sets the timestamp of the record, returns record
    item.data() -> returns the data (object) of the record
    item.data(<timestamp>) -> get the data (object) as it was at a specific time
    item.data('key') -> returns the data->key (value) of the record
    item.data('key', 'value') -> sets a key value pair of the data, returns the record
    item.data({object}) -> sets the data of the record, overrides old data, returns the record
    item.deltas() -> returns an array with delta objects (see delta)
    item.sync() -> syncs the record with the database and with the websocket
    

core specific:

    core.peer() -> returns our own peer object
    core.peerid()    -> returns our own peerid
    core.peerid(<string>) -> sets our own peerd
    core.user() -> returns the user object of currently logged on user (false when no user logged on)
    core.user(<string>) -> sets the current users id to the core and to the current peer, returns user object
    core.socketserver() -> returns the current socketserver configuration in use
    core.socketserver(<string>) -> sets the current socketserver
    core.websocket() -> returns the websocket object
    core.connect() -> start websocket connection, returns promise
    core.disconnect() -> closes the websocket connection (auto reconnect in 5 secs)
    core.location() -> returns location object of current peer
    core.location(obj) -> set location object of current peer, returns locations object
    core.activeUsers() -> returns array with userobjects that are currently active
    core.version() -> returns the current version number of cow

Since most methods return their own object, these methods are chainable. So you can write:

    var defaultproject = core.projects({}).data('name',"Sketch").sync();
    var defaultgroup = defaultproject.groups({}).data('name','Public').sync();
    var firstitem = defaultproject.items({})
        .data('type','msg')
        .data('creator',core.user().id())
        .sync();

The timestamp and dirtystatus are automatically updated when invoking the data() or deleted(true/false) method so you don't need to worry about that.

Some stores have deltas (invoked by item.deltas() ) which means that you can see a per-record history. Every change on an object (be it data or deleted status) is recorded as a delta and for the data only the new or updated data is being recorded (hence the name delta). A delta object contains:

{
 timestamp: <timestamp> //timestamp of time of update
 data: <object> //data object that has been changed as key-value pairs
 deleted: boolean //the deleted status at that moment
 userid: string or null //the userid of the user that made the change (if available)

Core

var cow = new Cow.core({
    herdname: 'test', //name of organisation
    maxage: 1000 * 60 * 60 * 24 * 30 //30 days in milliseconds
});

description initialise Cow and associate it with the matched element. The Cow object is refered to as cow in the documentation

maxage (maximum lifetime of objects to be stored, defaults to null)

  • peerid() -- get peerid
  • peerid(id) -- set peerid
  • project() -- get current project object
  • project(id) -- set current project based on id from projectStore
  • user() - get current user object
  • user(id) - set current user based on id from userStore
  • peer() - return my peer object
  • socketserver() - get the current socketserver configuration
  • location() - get the last known location
  • location(location) - set the current location
  • projectStore() - returns the _projectstore object
  • projects() - returns array of all projects
  • projects(id) - returns project with id (or null)
  • projects({config}) - creates and returns project
  • socketserverStore() - returns the _socketserverstore object
  • socketservers() - returns array of all socketservers
  • socketservers(id) - returns socketserver with id (or null)
  • socketservers({config}) - creates and returns socketserver object
  • peerStore() - returns the _peerstore object
  • peers() - returns array of all peers
  • peers(id) - returns peer with id (or null)
  • peers({config}) - creates and returns peer
  • userStore() - returns the _userstore object
  • users() - returns array of all users
  • users(id) - returns user with id (or null)
  • users({config}) - creates and returns user
  • activeUsers() - returns array with userobjects that are currently active
  • websocket() - return the _websocket object

Project

cow.Project(core, options)

description The Project object. It is constructed with the project options object in cow.projects(options). The Project object is refered to as project in the documentation. A Project has a group of zero or more Peers which share items, allowing for collaborative map-editing. There is one special Project, the 'sketch' project, the default, non-removable project; this one deletes features older than a week to prevent cluttering.

core: the cow object creating a project: core.projects(newid)

  • groupStore() - return groupStore object
  • groups() - return array of group objects
  • groups(id) - returns group with id
  • groups({options}) - creates and returns group object
  • itemStore() - return itemStore object
  • items() - return array of item objects
  • items(id) - returns item with id
  • items({options}) - creates and returns item object
  • myGroups() - return the group objects that I am member of

Group

  • members() - return array of member ids
  • members(id) - add id to member array, return group object
  • members([id]) - add id's to member array, return group object
  • removeMember(id) - remove id from array of member id's, return group object
  • removeAllMembers() - empty

Item

  • permissions() will return an array with all permissions set on this item
  • permissions('type') will return an array with the permission of type 'type'
  • permissions('type',group) will add the group to the permissions
  • of type 'type' (and create permission of type 'type' if needed), returns item
    
  • permissions('type',[group]) will add the array of groups to the permissions of type 'type' (and create permission of type 'type' if needed), returns item
  • permissionsHasGroup(type ,group ) - function to check if a particular type contains a particular group. Returns true if it is the case, false in all other cases
  • hasPermission() - check to see if current user has permission on item
  • removePermission('type') removes the entire permission type from the item
  • removePermission('type',[groups]) removes the groups from the permission type

Socketserver

cow.socketserver()

description The socketserver obejct. It is constructed with the socketserver options object in cow.socketservers(options). The socketserver object is refered to as socketserver in the documentation. A socketserver contains the configuration on all known websocket servers.

core: the cow object

  • url() - return the url to the socketserver, built up from the data elements: protocol, ip, port and directory

Peer

cow.peer(core, options)

description The Peer object. It is constructed with the peer options object in cow.peers(options). The Peer object is refered to as peer in the documentation. A Peer is another cow connected to the same websocket server (this can be someone else or the same user, using a different browser)

core: the cow object

  • user() - return id of currently connected user
  • user(id) - sets id of currently connected user, returns peer object

Websocket

cow.websocket(core, options)

description The Websocket object. It is constructed with the websocket options object in cow.websocket(options). The Websocket object is refered to as ws in the documentation. The Websocket object contains all the relevant info to make a websocket connection and manages the actual connection.

core: the cow object options: wsUrl

  • disconnect() - disconnect us from websocket server
  • connect(url) - connect to websocket server on url, returns connection
  • connection() - returns connection object
  • sendData(data, action, target) - send data to websocket server with params: data - json object action - string that describes the context of the message target - (optional) id of the target peer

Messaging

COW makes use of websocket messages to communicate with peers. A peer is not necessarely a COW instance but can be any client that adheres to websocket standards and the COW messaging protocol. See messaging.md and schema in docs for an overview of the message flow.

Dependencies Core

Underscore 1.6

http://underscorejs.org/underscore-min.js

polyfill-promise

https://github.com/jakearchibald/es6-promise

Dependencies Server

TODO

Known Issues

LICENSE

cow is licensed under the MIT license

cow's People

Contributors

bertt avatar fernandinand avatar luisbausalopez avatar oiseger avatar stvno avatar tomvantilburg avatar tomvantilburg-geodan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cow's Issues

Rename some names

Herd -> Project
Store -> Project (only in the messaging)
feature -> Item

Treat the map as a widget

The map is in fact nothing more than a replaceable screenwidget that displays the data that goes around in COW. Therefore all references to the map (e.g. OpenLayers lib) could be better put into the widget.
To keep in mind: some COW functionality depends on the map, for instance your viewextent that is being sent to other peers.

Add support for roles

This is a big one.
Start by designing a role/permission system.

Ultimately roles can be defined by any peer and propagated to other peers.
For a start roles can be baked into the code. An owner starts by selecting a role and is consequently presented with an interface with some functionality enabled based on the permissions for that role.

Extra functionality geolocator

Add options:
poll continuously/in intervals/no polling

Button to zoom to own location (also when no websocket available)

Sharing text messages

Text messages should be shared. This means a core update.

Make links between messages and map-features. That could be done in the clients via tagging.

Create GUI for sharing text (kind of chat interface)

Create populator add-in

Best would be when directly accessible from any polygon on the map.

Result should give an indication on the map of how many people are inside that polygon

Redraw d3 layers on mapCenter

When map is programmatically ceneterd (like after clicking on center to peers location) the d3 layers are not updated.

Admin panel/functionality needed

Functionality:

  • Set all feats to (un)deleted
  • Clear whole database
  • Restart nodejs
  • Set maximum age of features
  • See peer activity (movements, features etc)
  • Force peers to extent
  • Lock on peers extent

Make use of Identifications

Add an identification parameter to cow
Currently we have
CID which is the websocket ID, changes very often
UID which is the client ID, changes upon page reloa
add:
PID will be any string that identifies the person behind the client. Only changes on user request.

At the moment is is good enough to use the owner().name parameter as PID. It is not a big problem when people have the same PID but should be avoided in the future.

Objects that will make use of PID:
project.groups().members[pid list]
item.creator() -> pid
item.owner() -> pid
core.pid() -> pid

Add location to core object

me().location() is not available when disconnected. Therefore it should be (like username) be added to the core.

TODO for progideon

  • [OK] Add 'Add feature' functionality
  • [OK] Startpage for login and group choosing
  • [OK] Add extended label on hovering over features
  • [OK] Add logout button
  • [OK] Messages only visible when permitted (view or edit)
  • [OK] Add group info (colors) to messages
  • [WF] Add relevant icon / linecolor to messages
  • [OK] Add measurement tool
  • [OK] Make 'gedeeld beeld' functionality work:
    Fixed set of groups (o/p/e)

Add prioritization option for items

  • Items can have different priorities. (likely 1 to N)
  • Priorities can be entered from interface.
  • A priority filter can be applied to map/itemlist

Write a stresstest

Stresstest automates processes like adding/remove features, updating location, extent name etc...
Also usefull as a DEMO

Use local stored maptiles

Opanlayers is able to do this with indexeddb. This could dramatically increase map update speed and enhances offline use.

Add chat functionality

Involves new messagetype in cow and interface implementation.
How should messages be stored?
Should there be a link between messages and features?
Is a message very different from a feature anyway?

Needs to be implemented for IPO demo

Add alpha peer check

Peers should only broadcast 'answers' when they know they are the alpha peer.
With COW2 it is no longer needed for alpha to be inside the same project so we might be able to implement the old findalpha routine again in the websocket.js

Add icon set

  • Eenrichtingsverkeer aangeven dmv pijlen (liefst die je in de richting van de weg kunt draaien; als draaien niet kan, dan standaard borden met pijlen in elke windrichting; liefst witte pijlen gegoten in een blauwe achtergrond, zodat het lijkt op de 1 richtingsverkeerborden)
  • Afsluiten weg: verboden toegang verkeersbord
  • Populator resultaat: Aantal figuurtjes bij elkaar
  • Vee resultaat: plaatje van varken, schaap (ik weet ff niet meer welke dieren allemaal in het systeem zitten....)
  • Opvanglocaties: symbool van rode kruis of zoiets in gebouw? wellicht meerdere van dit soort symbolen: aparte kleuren voor wel/ niet geschikt voor gehandicapten en aparte grootte voor de grootte van de opvanglocatie; het symbool moet niet te verwarren zijn met ziekenhuis symbool
  • Dijkdoorbraak: symbool van kapotte dijk? of van waterdruppel?
  • Politie
  • GGD symbool
  • Ziekenhuis symbool
  • Brandweer
  • Gemeentehuis
  • Telecom mast als symbool voor ad hoc communicatiepaal
  • paar neutrale symbolen (gewoon vierkantje of rondje met verschillende of zo; voor het geval mensen zelf iets anders verzinnen)

Clean up cow.peer.js

There seems to be a lot more code going on than needed for adminstering the extent of the peer.

Improve D3 layer

Make a more robustly configurable maplayer.
Certainly fix the weird envelope folding behaviour of the extent.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.