Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Visualisation #387

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
17 changes: 17 additions & 0 deletions paravis/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# -------------------------------
# nw.js files
# -------------------------------
locales/
credits.html
icudtl.dat
*.bin
*.pak
nw
node_modules/

# -------------------------------
# project files
# -------------------------------
build/
res/fonts
lib/
3 changes: 3 additions & 0 deletions paravis/App.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"main":"MainActivity"
}
74 changes: 74 additions & 0 deletions paravis/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
Paravis
=======
Experimental Paranoid Visualizer

---

## Features ##
Paranoid Visualizer shows all actions occurring in
the cluster in a nice visual way

Features (both current and planned):
- [x] NWJS framework
- [x] Multiple Activity support
- [x] Transition support
- (only basic version is currently available, more features will be added)
- [ ] Downloading necessary binaries
- [ ] Filesystem operations
- [ ] Creating new filesystems
- [ ] Modifying existing ones
- [ ] Visualizing the data flow
- [x] Nodes
- [ ] Events

---

## Contributing ##
If you wish to contribute please make sure to read the steps outlined in the top level `CONTRIBUTING.md` file. The following rules only apply to __paravis__

Please clone the repo into `$GOPATH/src/github.com/cpssd/paranoid` as the backend is written in Go
and won't run outside `$GOPATH`

After cloning make sure to run `npm install`

Here are the steps to set up paravis for development:
To enable Chrome Dev Tools, download SDK build of nwjs available [here](http://nwjs.io).


The following instructions are just a matter of personal preference for
installing the NWJS SDK, you can install it wherever you feel appropriate for
your system. Please read over the commands:

1. Create a new folder: `sudo mkdir -p /opt/nwjs-sdk`
2. Navigate to the folder `cd /opt/nwjs-sdk`
3. Extract the nwjs SDK: `sudo tar xvf ~/Download/$nwjs-sdk.tar.gz`
4. Move everything to from extracted folder back: `sudo mv nwjs-sdk/* ./`
5. Create new group: `sudo groupadd sdkusers`
6. Add yourself to the group: `sudo usermod -aG sdkusers $USER`
7. Change nwjs permissions: `sudo chown -R :sdkusers .`
8. Copy the user permissions to group permissions using
`sudo chmod g+x $thing`, `sudo chmod g+r $thing` and similar.
9. Consult `man` pages for `chmod`
if you need help. You will get errors until you finish this step
10. Add SDK to PATH: `export PATH=$PATH:/opt/nwjs-sdk` (and remember to `source`)

---

## Building ##
If you wish to build __paravis__ into standalone application please follow the steps from official nwjs documentation available [here](http://docs.nwjs.io/en/latest/For%20Users/Package%20and%20Distribute/)

---

## Troubleshooting ##
You might run into problems while developing or running. Here is a compilation of the possible issues that were found. If you find more issues please make sure to report it with [Issues](https://github.com/cpssd/paranoid/issues)

#### I can't install start NWJS SDK ####
This is probably caused by bad permissions. Please make sure that `nw` can be executed, `.so`, `.pak`, `.bin` files read. Do not forget about the `locales` and `lib` folder. Just play around with it until it work ;)

#### I can't use nwbuild ####
This is most likely a issue with your permissions for npm. Not sure how to fix it :/

---

## Known issues ##
This list contains issues that are very hard to fix or we didn't really look at it yet
26 changes: 26 additions & 0 deletions paravis/_app/Activity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
class Activity {
constructor(){
console.log("Action initialized");
this.layout = "";
}

name(){
return "Activity";
}

layout(){
return this.layout;
}

onCreate() {
console.log("onCreate called");
}

startActivity(intent){
$(document.body).empty();
intent.onCreate();
}
}

// All activies must be registered
// activities[Activity.name] = Activity;
18 changes: 18 additions & 0 deletions paravis/_app/Dialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const DIALOG_TIMEOUT = 2000 //ms

class Dialog {
constructor(text, onClick, okButtonText, dismissButtonText){
this.m_element = document.createElement('dialog');
this.m_element.textContent = text;

this.m_element.innerHtml += document.createElement('ok')

setTimeout(() => {
this.Remove();
}, DIALOG_TIMEOUT);
}

Remove(){
$(this.m_element).remove();
}
}
22 changes: 22 additions & 0 deletions paravis/_app/Intent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

class Intent {
constructor(activity, transition){
this.transition = transition;
if(activity.name == A.mainActivity.name()){
console.error("This activity is already active");
return;
}
this.act = new activity();
}

onCreate(){
// Run the onCreate after the transition is done
this.transition.Run(() => {
this.act.onCreate();
})

}
}

var module = module || {exports: {}};
module.exports.Intent = Intent;
38 changes: 38 additions & 0 deletions paravis/_app/Popup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
class Popup {
constructor(title, layout){
var self = this;
console.log("Creating popup", title);

self.m_title = title;
self.m_layout = layout;
self.m_element = document.createElement('popup');
self.m_titleElement = document.createElement('title');
self.m_contentElement = document.createElement('content');

self.m_titleElement.textContent = this.m_title;
$(self.m_contentElement).load('/res/layout/popup/'+self.m_layout+'.html');

$(self.m_element).append(this.m_titleElement);
$(self.m_element).append(this.m_contentElement);

$(document).on('click', (e) => {
if(!$(e.target).closest(self.m_element)){
e.stopPropagation();
self.hide();
}
});
}

hide(){
$(this.m_element).remove();
}

onOk( callback ){
// Run callback when user presses ok
}

show(){
console.info('Showing popup')
$(document.body).append(this.m_element);
}
}
187 changes: 187 additions & 0 deletions paravis/_app/controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
var win = nw.Window.get();
var gui = require('nw.gui')
var fs = require('fs');
var app = require('../App.json');

// List of available activities
var activities = [];

var transitions = {};

var A = {
args: [], // CLI Argumens
strings: {}, // All available strings
name: gui.App.manifest.name, // Name of the application
version: gui.App.manifest.version, // Version of the application
debug: false, // Whether the debug mode is active
lang: "en", // Language of the applications. Default: en
mainActivity: {}, // Object storing the main acitity
activities: activities, // Array of all possible activities
disableCache: false, // Is cache disabled
};


// Perform all actions when the window loads
$(window).load(function(){
console.info("Initializing...");
init();

if(A.debug){
gui.showDevTools();
}

if(A.disableCache){
require.cache = {};
}

console.info("Startup finished");
});


// Initialize everything
function init(){
console.info("Starting main activity...");
A.mainActivity = new A.activities[app.main]();
console.info(app.main, "started.");

$(document.body).attr("activity", app.main);

// Get the CLI arguments
A.args = gui.App.argv;

// Show DevTools if --debug argument is given
if(A.args.indexOf("--debug") != -1 ){
A.debug = true;
}

// Determine what language is to be used. Default is english
var ix = 0;
if((ix = A.args.indexOf("--lang")) != -1){
A.lang = A.args[ix+1];
}

// Check is cache supposed to be ignored
if( A.args.indexOf('--no-cache') != -1 ){
console.log("Disabling cache");
A.disableCache = true;
}


console.info("Loading strings...");
// Load appropiate language text
var langFile = fs.readFileSync("res/strings/strings_"+A.lang+".json");
if(langFile == ""){
langFile = fs.readFileSync("res/strings/strings_en.json");
}
if(langFile == ""){
console.error("Unable to read strings file");
}

try {
A.strings = JSON.parse(langFile);
} catch(err){
console.Error("Unable to parse strings JSON:", err);
}
console.info("Finished loading strings.");

// Call onCreate function of the activity
A.mainActivity.onCreate();

handleCloseButton();
}

// handleActions wraps around other handle functions
function handleActions(){
handleLinks();
handleIncludes();
handleAppText();
handleCloseButton();
}

// Handle whenever there is an <<include> in code
function handleIncludes(){
var elem = {};
$("include").each(function(){
elem = $(this);
if(elem.text().length != 0){
return;
}
var layoutName = elem.attr("app:layout");
elem.load("/res/layout/"+layoutName+".html", () => handleActions());
console.info("Loaded", layoutName, "include");
});
}

// Handle app:text attribute
function handleAppText(){
var elem = {};
var textDescriptor = ["",""];
var items = document.body.getElementsByTagName("*");
for(var i = items.length; i--;){
elem = items[i];
if(!elem.hasAttribute("app:text")){
continue; // Don't persist if the element does not have the attribute
}
textDescriptor = elem.getAttribute("app:text").split(":");
switch(textDescriptor[0]){
case "app":
elem.innerHTML = A[textDescriptor[1]] + elem.innerHTML;
break;
case "string":
elem.innerHTML = A.strings[textDescriptor[1]] + elem.innerHTML;
break;
default:
break;
}
}
}

// Handle button to close the app
// TODO: Make it work...
function handleCloseButton(){
$('#app_close_button').on('click', () => {
console.log("Closing...");
win.close();
});
}

function handleLinks(){
$(document).on('click', (e) => {
var url = $(e.target).attr("app:link");
e.stopPropagation();
console.log(url);
if(url != null){
urlSplit = url.split(":");
switch(urlSplit[0]){
case "external":
console.info("Opening External Link...");
urlSplit.shift();
url = urlSplit.join(":");
gui.Shell.openExternal(url);
break;
case "app":
var i = new Intent(activities[urlSplit[1]], getTransition(urlSplit[2]));
A.mainActivity.startActivity(i);
break;
}
}
});
}

// Returns an appropriate transition
function getTransition(name){
if(name == null || name == undefined || name == ""){
return transitions.None;
}
if(transitions[name] == null){
return transitions.None;
}
return transitions[name];
}

// Loads the specific layout
function loadLayout(name){
console.log("Loading", name, "layout");
$(document.body).empty(); // Empty the body before writing to it
$(document.body).load("/res/layout/"+name+".html layout", () => handleActions());
}
Loading