mirror of
https://github.com/RetroShare/RetroShare.git
synced 2024-12-28 00:49:28 -05:00
Merge pull request #323 from zeners/webui
New webui frontend based on Mithril.js. Thx to Zener.
This commit is contained in:
commit
68b4cd860e
1
libresapi/src/.gitignore
vendored
Normal file
1
libresapi/src/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
webui/*
|
@ -2,13 +2,13 @@ libresapi: resource_api and new webinterface
|
||||
============================================
|
||||
|
||||
* ./api contains a C++ backend to control retroshare from webinterfaces or scripting
|
||||
* ./webfiles contains compiled files for the webinterface
|
||||
* ./webui contains HTML/CSS/JavaScript source files for the webinterface
|
||||
* ./webui contains compiled files for the webinterface (after build)
|
||||
* ./webui-src contains HTML/CSS/JavaScript source files for the webinterface (NEW, webinterface made with mithril.js)
|
||||
|
||||
Quickinfo for builders and packagers
|
||||
====================================
|
||||
|
||||
* copy the files in ./webfiles to
|
||||
* copy the files in ./webui to
|
||||
* ./webui (Windows)
|
||||
* /usr/share/RetroShare06/webui (Linux)
|
||||
* other OS: see RsAccountsDetail::PathDataDirectory()
|
@ -14,6 +14,7 @@
|
||||
#include "StateTokenServer.h" // for the state token serialisers
|
||||
|
||||
#include "ApiPluginHandler.h"
|
||||
#include "ChannelsHandler.h"
|
||||
|
||||
/*
|
||||
data types in json http://json.org/
|
||||
@ -226,13 +227,14 @@ class ApiServerMainModules
|
||||
public:
|
||||
ApiServerMainModules(ResourceRouter& router, StateTokenServer* sts, const RsPlugInInterfaces &ifaces):
|
||||
mPeersHandler(sts, ifaces.mNotify, ifaces.mPeers, ifaces.mMsgs),
|
||||
mIdentityHandler(ifaces.mIdentity),
|
||||
mIdentityHandler(sts, ifaces.mNotify, ifaces.mIdentity),
|
||||
mForumHandler(ifaces.mGxsForums),
|
||||
mServiceControlHandler(ifaces.mServiceControl),
|
||||
mFileSearchHandler(sts, ifaces.mNotify, ifaces.mTurtle, ifaces.mFiles),
|
||||
mTransfersHandler(sts, ifaces.mFiles),
|
||||
mChatHandler(sts, ifaces.mNotify, ifaces.mMsgs, ifaces.mPeers, ifaces.mIdentity, &mPeersHandler),
|
||||
mApiPluginHandler(sts, ifaces)
|
||||
mApiPluginHandler(sts, ifaces),
|
||||
mChannelsHandler(ifaces.mGxsChannels)
|
||||
{
|
||||
// the dynamic cast is to not confuse the addResourceHandler template like this:
|
||||
// addResourceHandler(derived class, parent class)
|
||||
@ -254,6 +256,8 @@ public:
|
||||
&ChatHandler::handleRequest);
|
||||
router.addResourceHandler("apiplugin", dynamic_cast<ResourceRouter*>(&mApiPluginHandler),
|
||||
&ChatHandler::handleRequest);
|
||||
router.addResourceHandler("channels", dynamic_cast<ResourceRouter*>(&mChannelsHandler),
|
||||
&ChannelsHandler::handleRequest);
|
||||
}
|
||||
|
||||
PeersHandler mPeersHandler;
|
||||
@ -264,6 +268,7 @@ public:
|
||||
TransfersHandler mTransfersHandler;
|
||||
ChatHandler mChatHandler;
|
||||
ApiPluginHandler mApiPluginHandler;
|
||||
ChannelsHandler mChannelsHandler;
|
||||
};
|
||||
|
||||
ApiServer::ApiServer():
|
||||
|
@ -305,7 +305,7 @@ public:
|
||||
|
||||
// get content-type from extension
|
||||
std::string ext = "";
|
||||
unsigned int i = info.fname.rfind('.');
|
||||
std::string::size_type i = info.fname.rfind('.');
|
||||
if(i != std::string::npos)
|
||||
ext = info.fname.substr(i+1);
|
||||
MHD_add_response_header(resp, "Content-Type", ContentTypes::cTypeFromExt(ext).c_str());
|
||||
|
112
libresapi/src/api/ChannelsHandler.cpp
Normal file
112
libresapi/src/api/ChannelsHandler.cpp
Normal file
@ -0,0 +1,112 @@
|
||||
#include "ChannelsHandler.h"
|
||||
|
||||
#include <retroshare/rsgxschannels.h>
|
||||
#include <util/radix64.h>
|
||||
#include <algorithm>
|
||||
#include "Operators.h"
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
|
||||
StreamBase& operator << (StreamBase& left, RsGxsFile& file)
|
||||
{
|
||||
left << makeKeyValueReference("name", file.mName)
|
||||
<< makeKeyValueReference("hash", file.mHash);
|
||||
if(left.serialise())
|
||||
{
|
||||
double size = file.mSize;
|
||||
left << makeKeyValueReference("size", size);
|
||||
}
|
||||
else
|
||||
{
|
||||
double size = 0;
|
||||
left << makeKeyValueReference("size", size);
|
||||
file.mSize = size;
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
ChannelsHandler::ChannelsHandler(RsGxsChannels *channels):
|
||||
mChannels(channels)
|
||||
{
|
||||
addResourceHandler("create_post", this, &ChannelsHandler::handleCreatePost);
|
||||
}
|
||||
|
||||
ResponseTask* ChannelsHandler::handleCreatePost(Request &req, Response &resp)
|
||||
{
|
||||
RsGxsChannelPost post;
|
||||
|
||||
req.mStream << makeKeyValueReference("group_id", post.mMeta.mGroupId);
|
||||
req.mStream << makeKeyValueReference("subject", post.mMeta.mMsgName);
|
||||
req.mStream << makeKeyValueReference("message", post.mMsg);
|
||||
|
||||
StreamBase& file_array = req.mStream.getStreamToMember("files");
|
||||
while(file_array.hasMore())
|
||||
{
|
||||
RsGxsFile file;
|
||||
file_array.getStreamToMember() << file;
|
||||
post.mFiles.push_back(file);
|
||||
}
|
||||
|
||||
std::string thumbnail_base64;
|
||||
req.mStream << makeKeyValueReference("thumbnail_base64_png", thumbnail_base64);
|
||||
|
||||
if(post.mMeta.mGroupId.isNull())
|
||||
{
|
||||
resp.setFail("groupd_id is null");
|
||||
return 0;
|
||||
}
|
||||
if(post.mMeta.mMsgName.empty())
|
||||
{
|
||||
resp.setFail("subject is empty");
|
||||
return 0;
|
||||
}
|
||||
if(post.mMsg.empty())
|
||||
{
|
||||
resp.setFail("msg text is empty");
|
||||
return 0;
|
||||
}
|
||||
// empty file list is ok, but files have to be valid
|
||||
for(std::list<RsGxsFile>::iterator lit = post.mFiles.begin(); lit != post.mFiles.end(); ++lit)
|
||||
{
|
||||
if(lit->mHash.isNull())
|
||||
{
|
||||
resp.setFail("at least one file hash is empty");
|
||||
return 0;
|
||||
}
|
||||
if(lit->mName.empty())
|
||||
{
|
||||
resp.setFail("at leats one file name is empty");
|
||||
return 0;
|
||||
}
|
||||
if(lit->mSize == 0)
|
||||
{
|
||||
resp.setFail("at least one file size is empty");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> png_data = Radix64::decode(thumbnail_base64);
|
||||
if(!png_data.empty())
|
||||
{
|
||||
if(png_data.size() < 8)
|
||||
{
|
||||
resp.setFail("Decoded thumbnail_base64_png is smaller than 8 byte. This can't be a valid png file!");
|
||||
return 0;
|
||||
}
|
||||
uint8_t png_magic_number[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
|
||||
if(!std::equal(&png_magic_number[0],&png_magic_number[8],png_data.begin()))
|
||||
{
|
||||
resp.setFail("Decoded thumbnail_base64_png does not seem to be a png file. (Header is missing magic number)");
|
||||
return 0;
|
||||
}
|
||||
post.mThumbnail.copy(png_data.data(), png_data.size());
|
||||
}
|
||||
|
||||
uint32_t token;
|
||||
mChannels->createPost(token, post);
|
||||
// TODO: grp creation acknowledge
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace resource_api
|
21
libresapi/src/api/ChannelsHandler.h
Normal file
21
libresapi/src/api/ChannelsHandler.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "ResourceRouter.h"
|
||||
|
||||
class RsGxsChannels;
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
|
||||
class ChannelsHandler : public ResourceRouter
|
||||
{
|
||||
public:
|
||||
ChannelsHandler(RsGxsChannels* channels);
|
||||
|
||||
private:
|
||||
ResponseTask* handleCreatePost(Request& req, Response& resp);
|
||||
|
||||
RsGxsChannels* mChannels;
|
||||
};
|
||||
|
||||
} // namespace resource_api
|
@ -97,6 +97,39 @@ StreamBase& operator << (StreamBase& left, ChatHandler::ChatInfo& info)
|
||||
return left;
|
||||
}
|
||||
|
||||
class SendLobbyParticipantsTask: public GxsResponseTask
|
||||
{
|
||||
public:
|
||||
SendLobbyParticipantsTask(RsIdentity* idservice, ChatHandler::LobbyParticipantsInfo pi):
|
||||
GxsResponseTask(idservice, 0), mParticipantsInfo(pi)
|
||||
{
|
||||
const std::map<RsGxsId, time_t>& map = mParticipantsInfo.participants;
|
||||
for(std::map<RsGxsId, time_t>::const_iterator mit = map.begin(); mit != map.end(); ++mit)
|
||||
{
|
||||
requestGxsId(mit->first);
|
||||
}
|
||||
}
|
||||
private:
|
||||
ChatHandler::LobbyParticipantsInfo mParticipantsInfo;
|
||||
protected:
|
||||
virtual void gxsDoWork(Request &req, Response &resp)
|
||||
{
|
||||
resp.mDataStream.getStreamToMember();
|
||||
const std::map<RsGxsId, time_t>& map = mParticipantsInfo.participants;
|
||||
for(std::map<RsGxsId, time_t>::const_iterator mit = map.begin(); mit != map.end(); ++mit)
|
||||
{
|
||||
StreamBase& stream = resp.mDataStream.getStreamToMember();
|
||||
double last_active = mit->second;
|
||||
stream << makeKeyValueReference("last_active", last_active);
|
||||
streamGxsId(mit->first, stream.getStreamToMember("identity"));
|
||||
}
|
||||
resp.mStateToken = mParticipantsInfo.state_token;
|
||||
resp.setOk();
|
||||
done();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
ChatHandler::ChatHandler(StateTokenServer *sts, RsNotify *notify, RsMsgs *msgs, RsPeers* peers, RsIdentity* identity, UnreadMsgNotify* unread):
|
||||
mStateTokenServer(sts), mNotify(notify), mRsMsgs(msgs), mRsPeers(peers), mRsIdentity(identity), mUnreadMsgNotify(unread), mMtx("ChatHandler::mMtx")
|
||||
{
|
||||
@ -111,11 +144,12 @@ ChatHandler::ChatHandler(StateTokenServer *sts, RsNotify *notify, RsMsgs *msgs,
|
||||
addResourceHandler("lobbies", this, &ChatHandler::handleLobbies);
|
||||
addResourceHandler("subscribe_lobby", this, &ChatHandler::handleSubscribeLobby);
|
||||
addResourceHandler("unsubscribe_lobby", this, &ChatHandler::handleUnsubscribeLobby);
|
||||
addResourceHandler("lobby_participants", this, &ChatHandler::handleLobbyParticipants);
|
||||
addResourceHandler("messages", this, &ChatHandler::handleMessages);
|
||||
addResourceHandler("send_message", this, &ChatHandler::handleSendMessage);
|
||||
addResourceHandler("mark_chat_as_read", this, &ChatHandler::handleMarkChatAsRead);
|
||||
addResourceHandler("info", this, &ChatHandler::handleInfo);
|
||||
addResourceHandler("typing_label", this, &ChatHandler::handleTypingLabel);
|
||||
addResourceHandler("receive_status", this, &ChatHandler::handleReceiveStatus);
|
||||
addResourceHandler("send_status", this, &ChatHandler::handleSendStatus);
|
||||
addResourceHandler("unread_msgs", this, &ChatHandler::handleUnreadMsgs);
|
||||
}
|
||||
@ -132,20 +166,21 @@ void ChatHandler::notifyChatMessage(const ChatMessage &msg)
|
||||
mRawMsgs.push_back(msg);
|
||||
}
|
||||
|
||||
// to be removed
|
||||
/*
|
||||
ChatHandler::Lobby ChatHandler::getLobbyInfo(ChatLobbyId id)
|
||||
void ChatHandler::notifyChatStatus(const ChatId &chat_id, const std::string &status)
|
||||
{
|
||||
tick();
|
||||
|
||||
RS_STACK_MUTEX(mMtx); // ********* LOCKED **********
|
||||
for(std::vector<Lobby>::iterator vit = mLobbies.begin(); vit != mLobbies.end(); ++vit)
|
||||
if(vit->id == id)
|
||||
return *vit;
|
||||
std::cerr << "ChatHandler::getLobbyInfo Error: Lobby not found" << std::endl;
|
||||
return Lobby();
|
||||
RS_STACK_MUTEX(mMtx); /********** LOCKED **********/
|
||||
locked_storeTypingInfo(chat_id, status);
|
||||
}
|
||||
|
||||
void ChatHandler::notifyChatLobbyEvent(uint64_t lobby_id, uint32_t event_type,
|
||||
const RsGxsId &nickname, const std::string& any_string)
|
||||
{
|
||||
RS_STACK_MUTEX(mMtx); /********** LOCKED **********/
|
||||
if(event_type == RS_CHAT_LOBBY_EVENT_PEER_STATUS)
|
||||
{
|
||||
locked_storeTypingInfo(ChatId(lobby_id), any_string, nickname);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void ChatHandler::tick()
|
||||
{
|
||||
@ -170,7 +205,44 @@ void ChatHandler::tick()
|
||||
l.is_broadcast = false;
|
||||
l.gxs_id = info.gxs_id;
|
||||
lobbies.push_back(l);
|
||||
|
||||
// update the lobby participants list
|
||||
// maybe it causes to much traffic to do this in every tick,
|
||||
// because the client would get the whole list every time a message was received
|
||||
// we could reduce the checking frequency
|
||||
std::map<ChatLobbyId, LobbyParticipantsInfo>::iterator mit = mLobbyParticipantsInfos.find(*lit);
|
||||
if(mit == mLobbyParticipantsInfos.end())
|
||||
{
|
||||
mLobbyParticipantsInfos[*lit].participants = info.gxs_ids;
|
||||
mLobbyParticipantsInfos[*lit].state_token = mStateTokenServer->getNewToken();
|
||||
}
|
||||
else
|
||||
{
|
||||
LobbyParticipantsInfo& pi = mit->second;
|
||||
if(!std::equal(pi.participants.begin(), pi.participants.end(), info.gxs_ids.begin()))
|
||||
{
|
||||
pi.participants = info.gxs_ids;
|
||||
mStateTokenServer->replaceToken(pi.state_token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove participants info of old lobbies
|
||||
std::vector<ChatLobbyId> participants_info_to_delete;
|
||||
for(std::map<ChatLobbyId, LobbyParticipantsInfo>::iterator mit = mLobbyParticipantsInfos.begin();
|
||||
mit != mLobbyParticipantsInfos.end(); ++mit)
|
||||
{
|
||||
if(std::find(subscribed_ids.begin(), subscribed_ids.end(), mit->first) == subscribed_ids.end())
|
||||
{
|
||||
participants_info_to_delete.push_back(mit->first);
|
||||
}
|
||||
}
|
||||
for(std::vector<ChatLobbyId>::iterator vit = participants_info_to_delete.begin(); vit != participants_info_to_delete.end(); ++vit)
|
||||
{
|
||||
LobbyParticipantsInfo& pi = mLobbyParticipantsInfos[*vit];
|
||||
mStateTokenServer->discardToken(pi.state_token);
|
||||
mLobbyParticipantsInfos.erase(*vit);
|
||||
}
|
||||
|
||||
{
|
||||
@ -439,6 +511,15 @@ void ChatHandler::getPlainText(const std::string& in, std::string &out, std::vec
|
||||
}
|
||||
}
|
||||
|
||||
void ChatHandler::locked_storeTypingInfo(const ChatId &chat_id, std::string status, RsGxsId lobby_gxs_id)
|
||||
{
|
||||
TypingLabelInfo& info = mTypingLabelInfo[chat_id];
|
||||
info.timestamp = time(0);
|
||||
info.status = status;
|
||||
mStateTokenServer->replaceToken(info.state_token);
|
||||
info.author_id = lobby_gxs_id;
|
||||
}
|
||||
|
||||
void ChatHandler::handleWildcard(Request &/*req*/, Response &resp)
|
||||
{
|
||||
RS_STACK_MUTEX(mMtx); /********** LOCKED **********/
|
||||
@ -496,11 +577,31 @@ void ChatHandler::handleSubscribeLobby(Request &req, Response &resp)
|
||||
resp.setFail("lobby join failed. (See console for more info)");
|
||||
}
|
||||
|
||||
void ChatHandler::handleUnsubscribeLobby(Request &req, Response &/*resp*/)
|
||||
void ChatHandler::handleUnsubscribeLobby(Request &req, Response &resp)
|
||||
{
|
||||
ChatLobbyId id = 0;
|
||||
req.mStream << makeKeyValueReference("id", id);
|
||||
mRsMsgs->unsubscribeChatLobby(id);
|
||||
resp.setOk();
|
||||
}
|
||||
|
||||
ResponseTask* ChatHandler::handleLobbyParticipants(Request &req, Response &resp)
|
||||
{
|
||||
RS_STACK_MUTEX(mMtx); /********** LOCKED **********/
|
||||
|
||||
ChatId id(req.mPath.top());
|
||||
if(!id.isLobbyId())
|
||||
{
|
||||
resp.setFail("Path element \""+req.mPath.top()+"\" is not a ChatLobbyId.");
|
||||
return 0;
|
||||
}
|
||||
std::map<ChatLobbyId, LobbyParticipantsInfo>::const_iterator mit = mLobbyParticipantsInfos.find(id.toLobbyId());
|
||||
if(mit == mLobbyParticipantsInfos.end())
|
||||
{
|
||||
resp.setFail("lobby not found");
|
||||
return 0;
|
||||
}
|
||||
return new SendLobbyParticipantsTask(mRsIdentity, mit->second);
|
||||
}
|
||||
|
||||
void ChatHandler::handleMessages(Request &req, Response &resp)
|
||||
@ -573,89 +674,6 @@ void ChatHandler::handleMarkChatAsRead(Request &req, Response &resp)
|
||||
mStateTokenServer->replaceToken(mUnreadMsgsStateToken);
|
||||
}
|
||||
|
||||
// to be removed
|
||||
// we do now cache chat info, to be able to include it in new message notify easily
|
||||
/*
|
||||
class InfoResponseTask: public GxsResponseTask
|
||||
{
|
||||
public:
|
||||
InfoResponseTask(ChatHandler* ch, RsPeers* peers, RsIdentity* identity): GxsResponseTask(identity, 0), mChatHandler(ch), mRsPeers(peers), mState(BEGIN){}
|
||||
|
||||
enum State {BEGIN, WAITING};
|
||||
ChatHandler* mChatHandler;
|
||||
RsPeers* mRsPeers;
|
||||
State mState;
|
||||
bool is_broadcast;
|
||||
bool is_gxs_id;
|
||||
bool is_lobby;
|
||||
bool is_peer;
|
||||
std::string remote_author_id;
|
||||
std::string remote_author_name;
|
||||
virtual void gxsDoWork(Request& req, Response& resp)
|
||||
{
|
||||
ChatId id(req.mPath.top());
|
||||
if(id.isNotSet())
|
||||
{
|
||||
resp.setFail("not a valid chat id");
|
||||
done();
|
||||
return;
|
||||
}
|
||||
if(mState == BEGIN)
|
||||
{
|
||||
is_broadcast = false;
|
||||
is_gxs_id = false;
|
||||
is_lobby = false;
|
||||
is_peer = false;
|
||||
if(id.isBroadcast())
|
||||
{
|
||||
is_broadcast = true;
|
||||
}
|
||||
else if(id.isGxsId())
|
||||
{
|
||||
is_gxs_id = true;
|
||||
remote_author_id = id.toGxsId().toStdString();
|
||||
requestGxsId(id.toGxsId());
|
||||
}
|
||||
else if(id.isLobbyId())
|
||||
{
|
||||
is_lobby = true;
|
||||
remote_author_id = "";
|
||||
remote_author_name = mChatHandler->getLobbyInfo(id.toLobbyId()).name;
|
||||
}
|
||||
else if(id.isPeerId())
|
||||
{
|
||||
is_peer = true;
|
||||
remote_author_id = id.toPeerId().toStdString();
|
||||
remote_author_name = mRsPeers->getPeerName(id.toPeerId());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Error in InfoResponseTask::gxsDoWork(): unhandled chat_id=" << id.toStdString() << std::endl;
|
||||
}
|
||||
mState = WAITING;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(is_gxs_id)
|
||||
remote_author_name = getName(id.toGxsId());
|
||||
resp.mDataStream << makeKeyValueReference("remote_author_id", remote_author_id)
|
||||
<< makeKeyValueReference("remote_author_name", remote_author_name)
|
||||
<< makeKeyValueReference("is_broadcast", is_broadcast)
|
||||
<< makeKeyValueReference("is_gxs_id", is_gxs_id)
|
||||
<< makeKeyValueReference("is_lobby", is_lobby)
|
||||
<< makeKeyValueReference("is_peer", is_peer);
|
||||
resp.setOk();
|
||||
done();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ResponseTask *ChatHandler::handleInfo(Request &req, Response &resp)
|
||||
{
|
||||
return new InfoResponseTask(this, mRsPeers, mRsIdentity);
|
||||
}
|
||||
*/
|
||||
|
||||
void ChatHandler::handleInfo(Request &req, Response &resp)
|
||||
{
|
||||
RS_STACK_MUTEX(mMtx); /********** LOCKED **********/
|
||||
@ -675,14 +693,99 @@ void ChatHandler::handleInfo(Request &req, Response &resp)
|
||||
resp.setOk();
|
||||
}
|
||||
|
||||
void ChatHandler::handleTypingLabel(Request &/*req*/, Response &/*resp*/)
|
||||
class SendTypingLabelInfo: public GxsResponseTask
|
||||
{
|
||||
public:
|
||||
SendTypingLabelInfo(RsIdentity* identity, RsPeers* peers, ChatId id, const ChatHandler::TypingLabelInfo& info):
|
||||
GxsResponseTask(identity), mState(BEGIN), mPeers(peers),mId(id), mInfo(info) {}
|
||||
private:
|
||||
enum State {BEGIN, WAITING_ID};
|
||||
State mState;
|
||||
RsPeers* mPeers;
|
||||
ChatId mId;
|
||||
ChatHandler::TypingLabelInfo mInfo;
|
||||
protected:
|
||||
void gxsDoWork(Request& /*req*/, Response& resp)
|
||||
{
|
||||
if(mState == BEGIN)
|
||||
{
|
||||
// lobby and distant require to fetch a gxs_id
|
||||
if(mId.isLobbyId())
|
||||
{
|
||||
requestGxsId(mInfo.author_id);
|
||||
}
|
||||
else if(mId.isDistantChatId())
|
||||
{
|
||||
DistantChatPeerInfo dcpinfo ;
|
||||
rsMsgs->getDistantChatStatus(mId.toDistantChatId(), dcpinfo);
|
||||
requestGxsId(dcpinfo.to_id);
|
||||
}
|
||||
mState = WAITING_ID;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string name = "BUG: case not handled in SendTypingLabelInfo";
|
||||
if(mId.isPeerId())
|
||||
{
|
||||
name = mPeers->getPeerName(mId.toPeerId());
|
||||
}
|
||||
else if(mId.isDistantChatId())
|
||||
{
|
||||
DistantChatPeerInfo dcpinfo ;
|
||||
rsMsgs->getDistantChatStatus(mId.toDistantChatId(), dcpinfo);
|
||||
name = getName(dcpinfo.to_id);
|
||||
}
|
||||
else if(mId.isLobbyId())
|
||||
{
|
||||
name = getName(mInfo.author_id);
|
||||
}
|
||||
else if(mId.isBroadcast())
|
||||
{
|
||||
name = mPeers->getPeerName(mId.broadcast_status_peer_id);
|
||||
}
|
||||
uint32_t ts = mInfo.timestamp;
|
||||
resp.mDataStream << makeKeyValueReference("author_name", name)
|
||||
<< makeKeyValueReference("timestamp", ts)
|
||||
<< makeKeyValueReference("status_string", mInfo.status);
|
||||
resp.mStateToken = mInfo.state_token;
|
||||
resp.setOk();
|
||||
done();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ResponseTask* ChatHandler::handleReceiveStatus(Request &req, Response &resp)
|
||||
{
|
||||
RS_STACK_MUTEX(mMtx); /********** LOCKED **********/
|
||||
ChatId id(req.mPath.top());
|
||||
if(id.isNotSet())
|
||||
{
|
||||
resp.setFail("\""+req.mPath.top()+"\" is not a valid chat id");
|
||||
return 0;
|
||||
}
|
||||
std::map<ChatId, TypingLabelInfo>::iterator mit = mTypingLabelInfo.find(id);
|
||||
if(mit == mTypingLabelInfo.end())
|
||||
{
|
||||
locked_storeTypingInfo(id, "");
|
||||
mit = mTypingLabelInfo.find(id);
|
||||
}
|
||||
return new SendTypingLabelInfo(mRsIdentity, mRsPeers, id, mit->second);
|
||||
}
|
||||
|
||||
void ChatHandler::handleSendStatus(Request &/*req*/, Response &/*resp*/)
|
||||
void ChatHandler::handleSendStatus(Request &req, Response &resp)
|
||||
{
|
||||
|
||||
std::string chat_id;
|
||||
std::string status;
|
||||
req.mStream << makeKeyValueReference("chat_id", chat_id)
|
||||
<< makeKeyValueReference("status", status);
|
||||
ChatId id(chat_id);
|
||||
if(id.isNotSet())
|
||||
{
|
||||
resp.setFail("chat_id is invalid");
|
||||
return;
|
||||
}
|
||||
mRsMsgs->sendStatusString(id, status);
|
||||
resp.setOk();
|
||||
}
|
||||
|
||||
void ChatHandler::handleUnreadMsgs(Request &/*req*/, Response &resp)
|
||||
|
@ -26,6 +26,13 @@ public:
|
||||
// note: this may get called from the own and from foreign threads
|
||||
virtual void notifyChatMessage(const ChatMessage& msg);
|
||||
|
||||
// typing label for peer, broadcast and distant chat
|
||||
virtual void notifyChatStatus (const ChatId& /* chat_id */, const std::string& /* status_string */);
|
||||
|
||||
//typing label for lobby chat, peer join and leave messages
|
||||
virtual void notifyChatLobbyEvent (uint64_t /* lobby id */, uint32_t /* event type */ ,
|
||||
const RsGxsId& /* nickname */,const std::string& /* any string */);
|
||||
|
||||
// from tickable
|
||||
virtual void tick();
|
||||
|
||||
@ -81,6 +88,12 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class LobbyParticipantsInfo{
|
||||
public:
|
||||
StateToken state_token;
|
||||
std::map<RsGxsId, time_t> participants;
|
||||
};
|
||||
|
||||
class ChatInfo{
|
||||
public:
|
||||
bool is_broadcast;
|
||||
@ -91,20 +104,32 @@ public:
|
||||
std::string remote_author_name;
|
||||
};
|
||||
|
||||
class TypingLabelInfo{
|
||||
public:
|
||||
time_t timestamp;
|
||||
std::string status;
|
||||
StateToken state_token;
|
||||
// only for lobbies
|
||||
RsGxsId author_id;
|
||||
};
|
||||
|
||||
private:
|
||||
void handleWildcard(Request& req, Response& resp);
|
||||
void handleLobbies(Request& req, Response& resp);
|
||||
void handleSubscribeLobby(Request& req, Response& resp);
|
||||
void handleUnsubscribeLobby(Request& req, Response& resp);
|
||||
ResponseTask* handleLobbyParticipants(Request& req, Response& resp);
|
||||
void handleMessages(Request& req, Response& resp);
|
||||
void handleSendMessage(Request& req, Response& resp);
|
||||
void handleMarkChatAsRead(Request& req, Response& resp);
|
||||
void handleInfo(Request& req, Response& resp);
|
||||
void handleTypingLabel(Request& req, Response& resp);
|
||||
ResponseTask *handleReceiveStatus(Request& req, Response& resp);
|
||||
void handleSendStatus(Request& req, Response& resp);
|
||||
void handleUnreadMsgs(Request& req, Response& resp);
|
||||
|
||||
void getPlainText(const std::string& in, std::string &out, std::vector<Triple> &links);
|
||||
// last parameter is only used for lobbies!
|
||||
void locked_storeTypingInfo(const ChatId& chat_id, std::string status, RsGxsId lobby_gxs_id = RsGxsId());
|
||||
|
||||
StateTokenServer* mStateTokenServer;
|
||||
RsNotify* mNotify;
|
||||
@ -121,9 +146,13 @@ private:
|
||||
|
||||
std::map<ChatId, ChatInfo> mChatInfo;
|
||||
|
||||
std::map<ChatId, TypingLabelInfo> mTypingLabelInfo;
|
||||
|
||||
StateToken mLobbiesStateToken;
|
||||
std::vector<Lobby> mLobbies;
|
||||
|
||||
std::map<ChatLobbyId, LobbyParticipantsInfo> mLobbyParticipantsInfos;
|
||||
|
||||
StateToken mUnreadMsgsStateToken;
|
||||
|
||||
};
|
||||
|
@ -4,6 +4,7 @@ class RsPlugInInterfaces;
|
||||
|
||||
namespace resource_api{
|
||||
|
||||
// populates the given RsPlugInInterfaces object with pointers from gloabl variables like rsPeers, rsMsgs, rsFiles...
|
||||
bool getPluginInterfaces(RsPlugInInterfaces& interfaces);
|
||||
|
||||
} // namespace resource_api
|
||||
|
@ -62,6 +62,7 @@ bool GxsResponseTask::doWork(Request &req, Response &resp)
|
||||
{
|
||||
more = false; // pause when an id failed, to give the service time tim fetch the data
|
||||
ready = false;
|
||||
// TODO: remove identities which failed many times from list, to avoid blocking when ids fail
|
||||
}
|
||||
}
|
||||
if(!ready)
|
||||
|
@ -15,7 +15,7 @@ class GxsResponseTask: public ResponseTask
|
||||
{
|
||||
public:
|
||||
// token service is allowed to be null if no token functions are wanted
|
||||
GxsResponseTask(RsIdentity* id_service, RsTokenService* token_service);
|
||||
GxsResponseTask(RsIdentity* id_service, RsTokenService* token_service = 0);
|
||||
virtual bool doWork(Request &req, Response& resp);
|
||||
|
||||
protected:
|
||||
|
@ -16,17 +16,18 @@ namespace resource_api
|
||||
class SendIdentitiesListTask: public GxsResponseTask
|
||||
{
|
||||
public:
|
||||
SendIdentitiesListTask(RsIdentity* idservice, std::list<RsGxsId> ids):
|
||||
GxsResponseTask(idservice, 0)
|
||||
SendIdentitiesListTask(RsIdentity* idservice, std::list<RsGxsId> ids, StateToken state):
|
||||
GxsResponseTask(idservice, 0), mStateToken(state)
|
||||
{
|
||||
for(std::list<RsGxsId>::iterator vit = ids.begin(); vit != ids.end(); ++vit)
|
||||
{
|
||||
requestGxsId(*vit);
|
||||
mIds.push_back(*vit);// convert fro list to vector
|
||||
mIds.push_back(*vit);// convert from list to vector
|
||||
}
|
||||
}
|
||||
private:
|
||||
std::vector<RsGxsId> mIds;
|
||||
StateToken mStateToken;
|
||||
protected:
|
||||
virtual void gxsDoWork(Request &req, Response &resp)
|
||||
{
|
||||
@ -35,17 +36,89 @@ protected:
|
||||
{
|
||||
streamGxsId(*vit, resp.mDataStream.getStreamToMember());
|
||||
}
|
||||
resp.mStateToken = mStateToken;
|
||||
resp.setOk();
|
||||
done();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
IdentityHandler::IdentityHandler(RsIdentity *identity):
|
||||
mRsIdentity(identity)
|
||||
class CreateIdentityTask: public GxsResponseTask
|
||||
{
|
||||
public:
|
||||
CreateIdentityTask(RsIdentity* idservice):
|
||||
GxsResponseTask(idservice, idservice->getTokenService()), mState(BEGIN), mToken(0), mRsIdentity(idservice){}
|
||||
private:
|
||||
enum State {BEGIN, WAIT_ACKN, WAIT_ID};
|
||||
State mState;
|
||||
uint32_t mToken;
|
||||
RsIdentity* mRsIdentity;
|
||||
RsGxsId mId;
|
||||
protected:
|
||||
virtual void gxsDoWork(Request &req, Response &resp)
|
||||
{
|
||||
switch(mState)
|
||||
{
|
||||
case BEGIN:{
|
||||
RsIdentityParameters params;
|
||||
req.mStream << makeKeyValueReference("name", params.nickname) << makeKeyValueReference("pgp_linked", params.isPgpLinked);
|
||||
|
||||
if(params.nickname == "")
|
||||
{
|
||||
resp.setFail("name can't be empty");
|
||||
done();
|
||||
return;
|
||||
}
|
||||
mRsIdentity->createIdentity(mToken, params);
|
||||
addWaitingToken(mToken);
|
||||
mState = WAIT_ACKN;
|
||||
break;
|
||||
}
|
||||
case WAIT_ACKN:{
|
||||
RsGxsGroupId grpId;
|
||||
if(!mRsIdentity->acknowledgeGrp(mToken, grpId))
|
||||
{
|
||||
resp.setFail("acknowledge of group id failed");
|
||||
done();
|
||||
return;
|
||||
}
|
||||
mId = RsGxsId(grpId);
|
||||
requestGxsId(mId);
|
||||
mState = WAIT_ID;
|
||||
break;
|
||||
}
|
||||
case WAIT_ID:
|
||||
streamGxsId(mId, resp.mDataStream);
|
||||
resp.setOk();
|
||||
done();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
IdentityHandler::IdentityHandler(StateTokenServer *sts, RsNotify *notify, RsIdentity *identity):
|
||||
mStateTokenServer(sts), mNotify(notify), mRsIdentity(identity),
|
||||
mMtx("IdentityHandler Mtx"), mStateToken(sts->getNewToken())
|
||||
{
|
||||
mNotify->registerNotifyClient(this);
|
||||
|
||||
addResourceHandler("*", this, &IdentityHandler::handleWildcard);
|
||||
addResourceHandler("own", this, &IdentityHandler::handleOwn);
|
||||
addResourceHandler("create_identity", this, &IdentityHandler::handleCreateIdentity);
|
||||
}
|
||||
|
||||
IdentityHandler::~IdentityHandler()
|
||||
{
|
||||
mNotify->unregisterNotifyClient(this);
|
||||
}
|
||||
|
||||
void IdentityHandler::notifyGxsChange(const RsGxsChanges &changes)
|
||||
{
|
||||
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
|
||||
// if changes come from identity service, invalidate own state token
|
||||
if(changes.mService == mRsIdentity->getTokenService())
|
||||
{
|
||||
mStateTokenServer->replaceToken(mStateToken);
|
||||
}
|
||||
}
|
||||
|
||||
void IdentityHandler::handleWildcard(Request &req, Response &resp)
|
||||
@ -54,6 +127,7 @@ void IdentityHandler::handleWildcard(Request &req, Response &resp)
|
||||
|
||||
if(req.isPut())
|
||||
{
|
||||
#ifdef REMOVE
|
||||
RsIdentityParameters params;
|
||||
req.mStream << makeKeyValueReference("name", params.nickname);
|
||||
if(req.mStream.isOK())
|
||||
@ -67,9 +141,14 @@ void IdentityHandler::handleWildcard(Request &req, Response &resp)
|
||||
{
|
||||
ok = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
|
||||
resp.mStateToken = mStateToken;
|
||||
}
|
||||
RsTokReqOptions opts;
|
||||
opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
|
||||
uint32_t token;
|
||||
@ -126,12 +205,22 @@ void IdentityHandler::handleWildcard(Request &req, Response &resp)
|
||||
|
||||
ResponseTask* IdentityHandler::handleOwn(Request &req, Response &resp)
|
||||
{
|
||||
StateToken state;
|
||||
{
|
||||
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
|
||||
state = mStateToken;
|
||||
}
|
||||
std::list<RsGxsId> ids;
|
||||
if(mRsIdentity->getOwnIds(ids))
|
||||
return new SendIdentitiesListTask(mRsIdentity, ids);
|
||||
return new SendIdentitiesListTask(mRsIdentity, ids, state);
|
||||
resp.mDataStream.getStreamToMember();
|
||||
resp.setWarning("identities not loaded yet");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ResponseTask* IdentityHandler::handleCreateIdentity(Request &req, Response &resp)
|
||||
{
|
||||
return new CreateIdentityTask(mRsIdentity);
|
||||
}
|
||||
|
||||
} // namespace resource_api
|
||||
|
@ -1,19 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <retroshare/rsnotify.h>
|
||||
#include <util/rsthreads.h>
|
||||
|
||||
#include "ResourceRouter.h"
|
||||
#include "StateTokenServer.h"
|
||||
|
||||
class RsIdentity;
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
|
||||
class IdentityHandler: public ResourceRouter
|
||||
class IdentityHandler: public ResourceRouter, NotifyClient
|
||||
{
|
||||
public:
|
||||
IdentityHandler(RsIdentity* identity);
|
||||
IdentityHandler(StateTokenServer* sts, RsNotify* notify, RsIdentity* identity);
|
||||
virtual ~IdentityHandler();
|
||||
|
||||
// from NotifyClient
|
||||
// note: this may get called from foreign threads
|
||||
virtual void notifyGxsChange(const RsGxsChanges &changes);
|
||||
|
||||
private:
|
||||
RsIdentity* mRsIdentity;
|
||||
void handleWildcard(Request& req, Response& resp);
|
||||
ResponseTask *handleOwn(Request& req, Response& resp);
|
||||
ResponseTask *handleCreateIdentity(Request& req, Response& resp);
|
||||
|
||||
StateTokenServer* mStateTokenServer;
|
||||
RsNotify* mNotify;
|
||||
RsIdentity* mRsIdentity;
|
||||
|
||||
RsMutex mMtx;
|
||||
StateToken mStateToken; // mutex protected
|
||||
};
|
||||
} // namespace resource_api
|
||||
|
@ -193,6 +193,8 @@ void PeersHandler::handleWildcard(Request &req, Response &resp)
|
||||
ok &= mRsPeers->getPeerDetails(*lit, details);
|
||||
detailsVec.push_back(details);
|
||||
}
|
||||
// mark response as list, in case it is empty
|
||||
resp.mDataStream.getStreamToMember();
|
||||
for(std::list<RsPgpId>::iterator lit = identities.begin(); lit != identities.end(); ++lit)
|
||||
{
|
||||
// if no own ssl id is known, then hide the own id from the friendslist
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
#include "Operators.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace resource_api
|
||||
{
|
||||
// maybe move to another place later
|
||||
@ -44,6 +46,7 @@ ServiceControlHandler::ServiceControlHandler(RsServiceControl* control):
|
||||
mRsServiceControl(control)
|
||||
{
|
||||
addResourceHandler("*", this, &ServiceControlHandler::handleWildcard);
|
||||
addResourceHandler("user", this, &ServiceControlHandler::handleUser);
|
||||
}
|
||||
|
||||
void ServiceControlHandler::handleWildcard(Request &req, Response &resp)
|
||||
@ -73,7 +76,42 @@ void ServiceControlHandler::handleWildcard(Request &req, Response &resp)
|
||||
}
|
||||
else if(req.isPut())
|
||||
{
|
||||
// change service default
|
||||
|
||||
std::string serviceidtext;
|
||||
bool enabled;
|
||||
|
||||
req.mStream << makeKeyValueReference("service_id", serviceidtext)
|
||||
<< makeKeyValueReference("default_allowed", enabled);
|
||||
|
||||
RsServicePermissions serv_perms ;
|
||||
//uint32_t serviceid = fromString<uint32_t>(serviceidtext);
|
||||
uint32_t serviceid = atoi(serviceidtext.c_str());
|
||||
if (serviceid == 0) {
|
||||
resp.setFail("service_id missed");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!rsServiceControl->getServicePermissions(serviceid, serv_perms)){
|
||||
resp.setFail("service_id " + serviceidtext + " is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
serv_perms.mDefaultAllowed = enabled;
|
||||
if(serv_perms.mDefaultAllowed)
|
||||
{
|
||||
serv_perms.mPeersDenied.clear() ;
|
||||
}
|
||||
else
|
||||
{
|
||||
serv_perms.mPeersAllowed.clear() ;
|
||||
}
|
||||
|
||||
ok = rsServiceControl->updateServicePermissions(serviceid,serv_perms);
|
||||
if (!ok) {
|
||||
resp.setFail("updateServicePermissions failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(ok)
|
||||
@ -86,4 +124,62 @@ void ServiceControlHandler::handleWildcard(Request &req, Response &resp)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ServiceControlHandler::handleUser(Request& req, Response& resp){
|
||||
// no get, only put (post) to allow user or delete to remove user
|
||||
|
||||
std::string serviceidtext;
|
||||
std::string peeridtext;
|
||||
bool enabled;
|
||||
bool ok;
|
||||
|
||||
req.mStream << makeKeyValueReference("service_id", serviceidtext)
|
||||
<< makeKeyValueReference("peer_id", peeridtext)
|
||||
<< makeKeyValueReference("enabled", enabled);
|
||||
|
||||
RsPeerId peer_id(peeridtext);
|
||||
|
||||
if (peer_id.isNull()) {
|
||||
resp.setFail("peer_id missing or not found");
|
||||
return;
|
||||
}
|
||||
|
||||
RsServicePermissions serv_perms ;
|
||||
uint32_t serviceid = atoi(serviceidtext.c_str());
|
||||
if (serviceid == 0) {
|
||||
resp.setFail("service_id missed");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!rsServiceControl->getServicePermissions(serviceid, serv_perms)){
|
||||
resp.setFail("service_id " + serviceidtext + " is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
if(req.isPut())
|
||||
{
|
||||
if (enabled && !serv_perms.peerHasPermission(peer_id))
|
||||
{
|
||||
serv_perms.setPermission(peer_id);
|
||||
} else if (!enabled && serv_perms.peerHasPermission(peer_id)){
|
||||
serv_perms.resetPermission(peer_id);
|
||||
} else {
|
||||
//nothing todo
|
||||
resp.setOk();
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
resp.setFail("only POST supported.");
|
||||
return;
|
||||
}
|
||||
ok = rsServiceControl->updateServicePermissions(serviceid,serv_perms);
|
||||
if (!ok) {
|
||||
resp.setFail("updateServicePermissions failed");
|
||||
return;
|
||||
}
|
||||
|
||||
resp.setOk();
|
||||
}
|
||||
|
||||
} // namespace resource_api
|
||||
|
@ -16,5 +16,6 @@ public:
|
||||
private:
|
||||
RsServiceControl* mRsServiceControl;
|
||||
void handleWildcard(Request& req, Response& resp);
|
||||
void handleUser(Request& req, Response& resp);
|
||||
};
|
||||
} // namespace resource_api
|
||||
|
@ -114,6 +114,7 @@ void StateTokenServer::handleWildcard(Request &req, Response &resp)
|
||||
RsStackMutex stack(mMtx); /********** STACK LOCKED MTX ******/
|
||||
// want to lookpup many tokens at once, return a list of invalid tokens
|
||||
// TODO: make generic list serialiser/deserialiser
|
||||
resp.mDataStream.getStreamToMember();
|
||||
while(req.mStream.hasMore())
|
||||
{
|
||||
StateToken token;
|
||||
|
@ -13,6 +13,7 @@ CONFIG += libmicrohttpd
|
||||
INCLUDEPATH += ../../libretroshare/src
|
||||
|
||||
unix {
|
||||
|
||||
webui_files.path = "$${DATA_DIR}/webui"
|
||||
webui_files.files = webfiles/*
|
||||
INSTALLS += webui_files
|
||||
@ -20,6 +21,13 @@ unix {
|
||||
webui_img_files.path = "$${DATA_DIR}/webui/img"
|
||||
webui_img_files.files = ../../retroshare-gui/src/gui/images/logo/logo_splash.png
|
||||
INSTALLS += webui_img_files
|
||||
|
||||
create_webfiles.commands = sh $$_PRO_FILE_PWD_/webui-src/make-src/build.sh $$_PRO_FILE_PWD_
|
||||
QMAKE_EXTRA_TARGETS += create_webfiles
|
||||
PRE_TARGETDEPS += create_webfiles
|
||||
|
||||
# create dummy files
|
||||
system(webui-src/make-src/init.sh .)
|
||||
}
|
||||
|
||||
win32{
|
||||
@ -66,7 +74,8 @@ SOURCES += \
|
||||
api/LivereloadHandler.cpp \
|
||||
api/TmpBlobStore.cpp \
|
||||
util/ContentTypes.cpp \
|
||||
api/ApiPluginHandler.cpp
|
||||
api/ApiPluginHandler.cpp \
|
||||
api/ChannelsHandler.cpp
|
||||
|
||||
HEADERS += \
|
||||
api/ApiServer.h \
|
||||
@ -91,4 +100,5 @@ HEADERS += \
|
||||
api/LivereloadHandler.h \
|
||||
api/TmpBlobStore.h \
|
||||
util/ContentTypes.h \
|
||||
api/ApiPluginHandler.h
|
||||
api/ApiPluginHandler.h \
|
||||
api/ChannelsHandler.h
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,109 +0,0 @@
|
||||
/**
|
||||
* JS Api for Retroshare
|
||||
* @constructor
|
||||
* @param {object} connection - an object which implements a request() function.
|
||||
* The request function should take two parameters: an object to be send as request and a callback.
|
||||
* The callback should get called with an response object on success.
|
||||
*/
|
||||
function RsApi(connection)
|
||||
{
|
||||
var runnign = true;
|
||||
/**
|
||||
* Send a request to the server
|
||||
* @param req - the request so send
|
||||
* @param {Function} cb - callback function which takes the response as parameter
|
||||
*/
|
||||
this.request = function(req, cb)
|
||||
{
|
||||
connection.request(req, cb);
|
||||
};
|
||||
var tokenlisteners = [];
|
||||
/**
|
||||
* Register a callback to be called when the state token expired.
|
||||
* @param {Function} listener - the callback function, which does not take arguments
|
||||
* @param token - the state token to listen for
|
||||
*/
|
||||
this.register_token_listener = function(listener, token)
|
||||
{
|
||||
tokenlisteners.push({listener:listener, token:token});
|
||||
};
|
||||
/**
|
||||
* Unregister a previously registered callback.
|
||||
*/
|
||||
this.unregister_token_listener = function(listener) // no token as parameter, assuming unregister from all listening tokens
|
||||
{
|
||||
var to_delete = [];
|
||||
for(var i=0; i<tokenlisteners.length; i++){
|
||||
if(tokenlisteners[i].listener === listener){
|
||||
to_delete.push(i);
|
||||
}
|
||||
}
|
||||
for(var i=0; i<to_delete.length; i++){
|
||||
// copy the last element to the current index
|
||||
var index = to_delete[i];
|
||||
tokenlisteners[index] = tokenlisteners[tokenlisteners.length-1];
|
||||
// remove last element
|
||||
tokenlisteners.pop();
|
||||
}
|
||||
};
|
||||
/**
|
||||
* start polling for state changes
|
||||
*/
|
||||
this.start = function(){
|
||||
running = true;
|
||||
setTimeout(tick, TICK_INTERVAL);
|
||||
}
|
||||
/**
|
||||
* stop polling for state changes
|
||||
*/
|
||||
this.stop = function(){
|
||||
running = false;
|
||||
}
|
||||
|
||||
// ************** interal stuff **************
|
||||
var TICK_INTERVAL = 3000;
|
||||
function received_tokenstates(resp)
|
||||
{
|
||||
if(resp.data){
|
||||
for(var i=0; i<resp.data.length; i++){
|
||||
var token = resp.data[i];
|
||||
// search the listener for this token
|
||||
for(var j=0; j<tokenlisteners.length; j++){
|
||||
if(tokenlisteners[j].token === token){
|
||||
// call the listener
|
||||
tokenlisteners[j].listener();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// schedule new update
|
||||
if(running)
|
||||
setTimeout(tick, TICK_INTERVAL);
|
||||
};
|
||||
function received_error()
|
||||
{
|
||||
// try again, maybe want a better logic later
|
||||
if(running)
|
||||
setTimeout(tick, TICK_INTERVAL);
|
||||
};
|
||||
function tick()
|
||||
{
|
||||
var data = [];
|
||||
// maybe cache the token list?
|
||||
// profiler will tell us if we should
|
||||
for(var i=0; i<tokenlisteners.length; i++){
|
||||
data.push(tokenlisteners[i].token);
|
||||
}
|
||||
connection.request({
|
||||
path: "statetokenservice",
|
||||
data: data,
|
||||
}, received_tokenstates, received_error);
|
||||
};
|
||||
};
|
||||
|
||||
// with this trick, we should be able to run in browser or nodejs
|
||||
if(typeof window === 'undefined')
|
||||
{
|
||||
// we are running in nodejs, so have to add to export
|
||||
module.exports = RsApi;
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
/**
|
||||
* Connection to the RS backend using XHR
|
||||
* (could add other connections later, for example WebSockets)
|
||||
* @constructor
|
||||
*/
|
||||
function RsXHRConnection(server_hostname, server_port)
|
||||
{
|
||||
var debug;
|
||||
//debug = function(str){console.log(str);};
|
||||
debug = function(str){};
|
||||
|
||||
//server_hostname = "localhost";
|
||||
//server_port = "9090";
|
||||
var api_root_path = "/api/v2/";
|
||||
|
||||
var status_listeners = [];
|
||||
|
||||
function notify_status(status)
|
||||
{
|
||||
for(var i = 0; i < status_listeners.length; i++)
|
||||
{
|
||||
status_listeners[i](status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be called when the state of the connection changes.
|
||||
* @param {function} cb - callback which receives a single argument. The arguments value is "connected" or "not_connected".
|
||||
*/
|
||||
this.register_status_listener = function(cb)
|
||||
{
|
||||
status_listeners.push(cb);
|
||||
};
|
||||
|
||||
/**
|
||||
* Unregister a status callback function.
|
||||
* @param {function} cb - a privously registered callback function
|
||||
*/
|
||||
this.unregister_status_listener = function(cb)
|
||||
{
|
||||
var to_delete = [];
|
||||
for(var i = 0; i < status_listeners.length; i++)
|
||||
{
|
||||
if(status_listeners[i] === cb){
|
||||
to_delete.push(i);
|
||||
}
|
||||
}
|
||||
for(var i = 0; i < to_delete.length; i++)
|
||||
{
|
||||
// copy the last element to the current index
|
||||
var index = to_delete[i];
|
||||
status_listeners[i] = status_listeners[status_listeners.length-1];
|
||||
// remove the last element
|
||||
status_listeners.pop();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a request to the backend
|
||||
* automatically encodes the request as JSON before sending it to the server
|
||||
* @param {object} req - the request to send to the server
|
||||
* @param {function} cb - callback function to be called to handle the response. The callback takes one object as parameter. Can be left undefined.
|
||||
* @param {function} err_cb - callback function to signal a failed request. Can be undefined.
|
||||
*/
|
||||
this.request = function(req, cb, err_cb)
|
||||
{
|
||||
//var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
|
||||
// TODO: window is not available in QML
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = function(){
|
||||
//console.log("onreadystatechanged state"+xhr.readyState);
|
||||
// TODO: figure out how to catch errors like connection refused
|
||||
// maybe want to have to set a state variable like ok=false
|
||||
// the gui could then display: "no connection to server"
|
||||
if (xhr.readyState === 4) {
|
||||
if(xhr.status !== 200)
|
||||
{
|
||||
console.log("RsXHRConnection: request failed with status: "+xhr.status);
|
||||
console.log("request was:");
|
||||
console.log(req);
|
||||
notify_status("not_connected");
|
||||
if(err_cb !== undefined)
|
||||
err_cb();
|
||||
return;
|
||||
}
|
||||
// received response
|
||||
notify_status("connected");
|
||||
debug("RsXHRConnection received response:");
|
||||
debug(xhr.responseText);
|
||||
if(false)//if(xhr.responseText === "")
|
||||
{
|
||||
debug("Warning: response is empty");
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
var respObj = JSON.parse(xhr.responseText);
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
debug("Exception during response handling: "+e);
|
||||
}
|
||||
if(cb === undefined)
|
||||
debug("No callback function specified");
|
||||
else
|
||||
cb(respObj);
|
||||
}
|
||||
}
|
||||
// post is required for sending data
|
||||
var method;
|
||||
if(req.data){
|
||||
method = "POST";
|
||||
} else {
|
||||
method = "GET";
|
||||
}
|
||||
xhr.open(method, "http://"+server_hostname+":"+server_port+api_root_path+req.path);
|
||||
var data = JSON.stringify(req.data);
|
||||
debug("RsXHRConnection sending data:");
|
||||
debug(data);
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
xhr.send(data);
|
||||
};
|
||||
};
|
@ -1,134 +0,0 @@
|
||||
body {
|
||||
background-color: black;
|
||||
color: lime;
|
||||
font-family: monospace;
|
||||
margin: 0em;
|
||||
/*padding: 1.5em;*/
|
||||
padding: 2mm;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
#overlay{
|
||||
z-index: 10;
|
||||
position: fixed;
|
||||
top:0;
|
||||
left:0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0,0,0,0.8);
|
||||
}
|
||||
|
||||
.paddingbox{
|
||||
padding:2mm;
|
||||
}
|
||||
|
||||
.nav{
|
||||
list-style-type: none;
|
||||
padding: 0em;
|
||||
margin: 0em;
|
||||
}
|
||||
|
||||
.nav li{
|
||||
display: inline;
|
||||
padding: 0.1em;
|
||||
margin-right: 1em;
|
||||
border-width: 0.1em;
|
||||
border-color: blue;
|
||||
border-bottom-style: solid;
|
||||
cursor: pointer;
|
||||
}
|
||||
td{
|
||||
padding: 0.3em;
|
||||
border-style: solid;
|
||||
border-width: 0.1em;
|
||||
border-color: lime;
|
||||
}
|
||||
.btn{
|
||||
border-style: solid;
|
||||
border-color: lime;
|
||||
border-width: 0.1em;
|
||||
cursor: pointer;
|
||||
padding: 0.1em;
|
||||
}
|
||||
|
||||
.btn2, .box{
|
||||
border-style: solid;
|
||||
/*border-color: lime;*/
|
||||
border-color: limeGreen;
|
||||
/*border-width: 1px;*/
|
||||
border-radius: 3mm;
|
||||
padding: 2mm;
|
||||
font-size: 10mm;
|
||||
cursor: pointer;
|
||||
margin-bottom: 2mm;
|
||||
}
|
||||
.btn2:hover{
|
||||
background-color: midnightblue;
|
||||
}
|
||||
|
||||
.filelink{
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
input, textarea{
|
||||
color: lime;
|
||||
font-family: monospace;
|
||||
background-color: black;
|
||||
border-color: lime;
|
||||
font-size: 10mm;
|
||||
border-radius: 3mm;
|
||||
border-width: 1mm;
|
||||
padding: 2mm;
|
||||
margin-bottom: 2mm;
|
||||
margin-right: 2mm;
|
||||
|
||||
/* make the button the whole screen width */
|
||||
width: 100%;
|
||||
/* make the text input fit small screens*/
|
||||
box-sizing: border-box;
|
||||
}
|
||||
input:hover{
|
||||
background-color: midnightblue;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.flexbox{
|
||||
display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */
|
||||
display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */
|
||||
display: -ms-flexbox; /* TWEENER - IE 10 */
|
||||
display: -webkit-flex; /* NEW - Chrome */
|
||||
display: flex; /* NEW, Spec - Opera 12.1, Firefox 20+ */
|
||||
}
|
||||
|
||||
.flexwidemember{
|
||||
-webkit-box-flex: 1; /* OLD - iOS 6-, Safari 3.1-6 */
|
||||
-moz-box-flex: 1; /* OLD - Firefox 19- */
|
||||
width: 20%; /* For old syntax, otherwise collapses. */
|
||||
-webkit-flex: 1; /* Chrome */
|
||||
-ms-flex: 1; /* IE 10 */
|
||||
flex: 1; /* NEW, Spec - Opera 12.1, Firefox 20+ */
|
||||
}
|
||||
|
||||
#logo_splash{
|
||||
-webkit-animation-fill-mode: forwards; /* Chrome, Safari, Opera */
|
||||
animation-fill-mode: forwards;
|
||||
-webkit-animation-name: logo_splash; /* Chrome, Safari, Opera */
|
||||
-webkit-animation-duration: 3s; /* Chrome, Safari, Opera */
|
||||
animation-name: logo_splash;
|
||||
animation-duration: 3s;
|
||||
text-align: center;
|
||||
}
|
||||
/* Chrome, Safari, Opera */
|
||||
@-webkit-keyframes logo_splash {
|
||||
from {opacity: 0;}
|
||||
to {opacity: 1;}
|
||||
}
|
||||
|
||||
/* Standard syntax */
|
||||
@keyframes logo_splash {
|
||||
from {opacity: 0;}
|
||||
to {opacity: 1;}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,29 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>New webinterface for Retroshare</title>
|
||||
|
||||
<script src="RsXHRConnection.js"></script>
|
||||
<script src="RsApi.js"></script>
|
||||
|
||||
<!-- it seems to work more reliable, if the jsx file is loaded before react -->
|
||||
<script type="text/jsx" src="gui.jsx"></script>
|
||||
|
||||
<script src="react.js"></script>
|
||||
<script src="JSXTransformer.js"></script>
|
||||
|
||||
<link href="green-black.css" rel="stylesheet">
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
document.write("<p>loading lots of stuff...</p>");
|
||||
</script>
|
||||
<p><noscript>The Retroshare web interface requires JavaScript. Please enable JavaScript in your browser.</noscript></p>
|
||||
<!--<div id="logo_splash">
|
||||
<img src="img/logo_splash.png"></img>
|
||||
</div>-->
|
||||
</body>
|
||||
</html>
|
19541
libresapi/src/webfiles/react.js
vendored
19541
libresapi/src/webfiles/react.js
vendored
File diff suppressed because it is too large
Load Diff
2
libresapi/src/webui-src/.gitignore
vendored
Normal file
2
libresapi/src/webui-src/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules/*
|
||||
public/*
|
78
libresapi/src/webui-src/README.md
Normal file
78
libresapi/src/webui-src/README.md
Normal file
@ -0,0 +1,78 @@
|
||||
A new approach to build a webinterface for RS
|
||||
=============================================
|
||||
|
||||
1. get JSON encoded data from the backend, data contains a state token
|
||||
2. render data with mithril.js
|
||||
3. ask the backend if the state token from step 1 expired. If yes, then start again with step 1.
|
||||
|
||||
Steps 1. and 3. are common for most things, only Step 2. differs. This allows to re-use code for steps 1. and 3.
|
||||
|
||||
BUILD / DEVELOPMENT
|
||||
------------
|
||||
|
||||
- install tools
|
||||
sudo apt-get install TODO (insert package names for nodejs, ruby, sass here)
|
||||
- run this once in webui-src directory, to install more tools
|
||||
npm install
|
||||
- start build
|
||||
npm run watch
|
||||
- the build process watches files for changes, and rebuilds and reloads the page. Build output is in ./public
|
||||
- use the --webinterface 9090 command line parameter to enable webui in retroshare-nogui
|
||||
- set the --docroot parameter of retroshare-nogui to point to the "libresapi/src/webui-src/public" directory
|
||||
(or symlink from /usr/share/RetroShare06/webui on Linux, ./webui on Windows)
|
||||
- retroshare-gui does not have a --docroot parameter. Use symlinks then.
|
||||
|
||||
CONTRIBUTE
|
||||
----------
|
||||
|
||||
- if you are a web developer or want to become one
|
||||
get in contact!
|
||||
- lots of work to do, i need you!
|
||||
|
||||
TODO
|
||||
----
|
||||
[ ] make stylesheets or find reusable sass/css components
|
||||
google material design has nice rules for color, spacing and everything: https://www.google.de/design/spec/material-design/introduction.html
|
||||
[ ] find icons, maybe use google material design iconfont
|
||||
[X] use urls/mithril routing for the menu. urls could replace state stored in rs.content
|
||||
[X] drag and drop private key upload and import
|
||||
[X] link from peer location to chat (use urls and mithril routing)
|
||||
[X] add/remove friend, own cert
|
||||
[X] downloads, search
|
||||
[ ] make reusable infinite list controller, the js part to load data from Pagination.h (tweak Pagination.h to make everything work)
|
||||
should provide forward, backward and follow-list-end
|
||||
[ ] backend: view/create identities
|
||||
[ ] backend: chat lobby participants list
|
||||
[X] chat: send_message
|
||||
[ ] backend: chat typing notifications
|
||||
[ ] make routines to handle retroshare links
|
||||
[ ] backend: edit shared folders
|
||||
[ ] backend: view shared files
|
||||
[ ] redirect if a url is not usable in the current runstate (e.g. redirect from login page to home page, after login)
|
||||
[X] sort friendslist
|
||||
|
||||
need 4 master
|
||||
-------------
|
||||
[X] unsubscribe lobby
|
||||
[X] unread chat message counter in menu
|
||||
[X] list chat-lobby participants
|
||||
[X] creating app.js on build (no need for npm on regulary build)
|
||||
|
||||
url-handling (brainstorming)
|
||||
----------------------------
|
||||
* normal weblinks (bbcode? => only with gui support)
|
||||
* rslinks
|
||||
- files
|
||||
- (chatrooms)
|
||||
- forum retroshare://forum?name=Developers%27%20Discussions&id=8fd22bd8f99754461e7ba1ca8a727995
|
||||
- own cert link (paste)
|
||||
- cert-links
|
||||
- searches
|
||||
- [X] downloads pasten
|
||||
- uploads?
|
||||
* enter / display urls
|
||||
- use urls in href like used for input (so it can be copy-link)
|
||||
- handle RS-urls with javascript, other with target _blank
|
||||
* smilies
|
||||
* Bilder
|
||||
* KEEP IT SIMPLE
|
81
libresapi/src/webui-src/app/_chat.sass
Normal file
81
libresapi/src/webui-src/app/_chat.sass
Normal file
@ -0,0 +1,81 @@
|
||||
.chat
|
||||
$color: black
|
||||
$header_height: 50px
|
||||
$left_width: 200px
|
||||
$right_width: 200px
|
||||
$input_height: 100px
|
||||
padding: 15px
|
||||
&.container
|
||||
height: 100%
|
||||
padding: 0px
|
||||
position: relative
|
||||
box-sizing: border-box
|
||||
&.header
|
||||
position: absolute
|
||||
top: 0px
|
||||
left: 0px
|
||||
right: 0px
|
||||
height: $header_height
|
||||
background-color: $color
|
||||
border-bottom: solid 1px gray
|
||||
box-sizing: border-box
|
||||
&.left
|
||||
position: absolute
|
||||
top: $header_height
|
||||
bottom: 0px
|
||||
left: 0px
|
||||
width: $left_width
|
||||
//border-right: solid 1px gray
|
||||
box-sizing: border-box
|
||||
background-color: black
|
||||
&.right
|
||||
position: absolute
|
||||
top: $header_height
|
||||
right: 0px
|
||||
bottom: 0px
|
||||
width: $right_width
|
||||
box-sizing: border-box
|
||||
//border-left: solid 1px gray
|
||||
&.middle
|
||||
//background-color: blue
|
||||
position: absolute
|
||||
top: 0px
|
||||
margin-top: $header_height
|
||||
left: $left_width
|
||||
right: $right_width
|
||||
box-sizing: border-box
|
||||
padding: 0px
|
||||
height: 100%
|
||||
overflow-y: scroll
|
||||
&.bottom
|
||||
position: absolute
|
||||
bottom: 0px
|
||||
right: $right_width
|
||||
left: $left_width
|
||||
padding: 5px
|
||||
&.msg
|
||||
padding: 0px
|
||||
$author_width: 100px
|
||||
&.container
|
||||
position: relative
|
||||
border-bottom: solid 1px lightgray
|
||||
padding: 10px
|
||||
height: unset
|
||||
//background-color: lime
|
||||
&.from
|
||||
position: absolute
|
||||
width: $author_width
|
||||
top: 10px
|
||||
left: 0px
|
||||
color: white
|
||||
text-align: right
|
||||
&.when
|
||||
float: right
|
||||
color: lightgray
|
||||
margin-bottom: 10px
|
||||
&.text
|
||||
padding-left: $author_width
|
||||
top: 0px
|
||||
left: $author_width
|
||||
white-space: pre-wrap
|
||||
height: initial
|
48
libresapi/src/webui-src/app/_reset.scss
Normal file
48
libresapi/src/webui-src/app/_reset.scss
Normal file
@ -0,0 +1,48 @@
|
||||
/* http://meyerweb.com/eric/tools/css/reset/
|
||||
v2.0 | 20110126
|
||||
License: none (public domain)
|
||||
*/
|
||||
|
||||
html, body, div, span, applet, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
a, abbr, acronym, address, big, cite, code,
|
||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var,
|
||||
b, u, i, center,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||
article, aside, canvas, details, embed,
|
||||
figure, figcaption, footer, header, hgroup,
|
||||
menu, nav, output, ruby, section, summary,
|
||||
time, mark, audio, video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
/* HTML5 display-role reset for older browsers */
|
||||
article, aside, details, figcaption, figure,
|
||||
footer, header, hgroup, menu, nav, section {
|
||||
display: block;
|
||||
}
|
||||
body {
|
||||
line-height: 1;
|
||||
}
|
||||
ol, ul {
|
||||
list-style: none;
|
||||
}
|
||||
blockquote, q {
|
||||
quotes: none;
|
||||
}
|
||||
blockquote:before, blockquote:after,
|
||||
q:before, q:after {
|
||||
content: '';
|
||||
content: none;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
68
libresapi/src/webui-src/app/accountselect.js
Normal file
68
libresapi/src/webui-src/app/accountselect.js
Normal file
@ -0,0 +1,68 @@
|
||||
"use strict";
|
||||
|
||||
var m = require("mithril");
|
||||
var rs = require("retroshare");
|
||||
|
||||
function cancel(){
|
||||
rs.memory("control/locations").curraccount=null;
|
||||
m.redraw();
|
||||
}
|
||||
|
||||
function selAccount(account){
|
||||
rs.memory("control/locations").curraccount=account;
|
||||
m.redraw();
|
||||
rs.request("control/login", {id: account.id}, function(){
|
||||
console.log("login sent");
|
||||
});
|
||||
}
|
||||
|
||||
function curraccount() {
|
||||
var mem;
|
||||
mem = rs.memory("control/locations");
|
||||
if (mem.curraccount === undefined) {
|
||||
return null;
|
||||
}
|
||||
return mem.curraccount;
|
||||
}
|
||||
|
||||
module.exports = {view: function(){
|
||||
var accounts = rs("control/locations");
|
||||
if(accounts === undefined || accounts == null){
|
||||
return m("div", "accounts: waiting_server");
|
||||
}
|
||||
if (curraccount() == null) {
|
||||
return m("div", [
|
||||
m("h2","login:"),
|
||||
m("hr"),
|
||||
accounts.map(function(account){
|
||||
return [
|
||||
m("div.btn2", {
|
||||
onclick: function(){
|
||||
selAccount(account)
|
||||
}
|
||||
},
|
||||
account.location + " (" + account.name + ")"),
|
||||
m("br")
|
||||
]
|
||||
})
|
||||
]);
|
||||
} else {
|
||||
// rs.untoken("control/password");
|
||||
return m("div", [
|
||||
m("div", [
|
||||
"logging in ...",
|
||||
m("br"),
|
||||
"(waiting for password-request)",
|
||||
]),
|
||||
/*
|
||||
m("hr"),
|
||||
m(".btn2", {
|
||||
onclick: function() {
|
||||
cancel();
|
||||
}
|
||||
},"Cancel " + curraccount().name + " login "),
|
||||
*/
|
||||
]);
|
||||
}
|
||||
}
|
||||
};
|
299
libresapi/src/webui-src/app/adddownloads.js
Normal file
299
libresapi/src/webui-src/app/adddownloads.js
Normal file
@ -0,0 +1,299 @@
|
||||
var m = require("mithril");
|
||||
var rs = require("retroshare");
|
||||
|
||||
var me = {
|
||||
toParse: [], // links to parse ( = pasted content)
|
||||
toConfirm: [], // links to confirm
|
||||
toAdd: [], // links to add
|
||||
toResult: [], // Result to show
|
||||
index: 0,
|
||||
view: function(){
|
||||
return m("div", {
|
||||
style: {
|
||||
height:"100%",
|
||||
boxSizing: "border-box",
|
||||
paddingBottom: "130px",
|
||||
}
|
||||
},[
|
||||
m("h2","add downloads"),
|
||||
m("hr"),
|
||||
this.toParse.length
|
||||
? step2()
|
||||
: this.toConfirm.length
|
||||
? step3()
|
||||
: this.toAdd.length
|
||||
? step4()
|
||||
: this.toResult.length
|
||||
? step5()
|
||||
: step1()
|
||||
,
|
||||
]);
|
||||
},
|
||||
parseOne: function(){
|
||||
if (me.index == null) {
|
||||
return null;
|
||||
}
|
||||
var startindex = me.index;
|
||||
while (me.toParse.length > me.index && me.index - startindex < 10) {
|
||||
var src = me.toParse[me.index].split("?",2);
|
||||
console.log(src);
|
||||
if (src[0] == "retroshare://file" && src.length == 2) {
|
||||
var target = {action: "begin"};
|
||||
var errText = "Error: link missing name and/or hash"
|
||||
src[1].split("&").map(function(parm){
|
||||
var pos = parm.indexOf("=");
|
||||
if (pos >0){
|
||||
if (parm.substr(0,pos) == "name") {
|
||||
var sname=decodeURIComponent(parm.substr(pos+1))
|
||||
if (sname.match("[\\\\/]")) {
|
||||
errText="name contains illegal char "
|
||||
+ sname.match("[\\\\/]");
|
||||
} else {
|
||||
target.name=sname;
|
||||
}
|
||||
} else if (parm.substr(0,pos) == "size") {
|
||||
target.size=parseFloat(parm.substr(pos+1));
|
||||
} else if (parm.substr(0,pos) == "hash") {
|
||||
target.hash=parm.substr(pos+1);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (target['name'] && target['hash']){
|
||||
me.toConfirm.push({
|
||||
text: target.name,
|
||||
target: target,
|
||||
confirmed: true,
|
||||
});
|
||||
} else {
|
||||
me.toConfirm.push({
|
||||
text:errText,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
me.toConfirm.push({ text: "Error: no Retroshare-file link"})
|
||||
}
|
||||
me.index++;
|
||||
}
|
||||
if (me.toParse.length > me.index) {
|
||||
window.setTimeout("require(\"adddownloads\").parseOne()",1);
|
||||
} else {
|
||||
me.toParse = [];
|
||||
console.log(me.toConfirm.length);
|
||||
}
|
||||
refresh();
|
||||
},
|
||||
addOne: function(){
|
||||
if (me.index == null) {
|
||||
cancel();
|
||||
} else if (me.index >= me.toAdd.length) {
|
||||
me.toResult=me.toAdd;
|
||||
me.toAdd=[];
|
||||
refresh();
|
||||
} else {
|
||||
console.log([
|
||||
me.toAdd[me.index].action,
|
||||
me.toAdd[me.index].name,
|
||||
me.toAdd[me.index].size,
|
||||
me.toAdd[me.index].hash,
|
||||
]);
|
||||
refresh();
|
||||
rs.request("transfers/control_download", me.toAdd[me.index],
|
||||
function(data,statetoken){
|
||||
if (me.index != null) {
|
||||
me.toAdd[me.index].ok=true;
|
||||
me.index++;
|
||||
me.addOne();
|
||||
}
|
||||
}, {
|
||||
onfail: function(value){
|
||||
me.toAdd[me.index].ok=false;
|
||||
me.toAdd[me.index].debug_msg=value;
|
||||
me.index++;
|
||||
me.addOne();
|
||||
},
|
||||
onmismatch: function(response){
|
||||
me.toAdd[me.index].ok=false;
|
||||
me.toAdd[me.index].debug_msg=response.debug_msg;
|
||||
me.index++;
|
||||
me.addOne();
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function cancel() {
|
||||
me.toAdd=[];
|
||||
me.toConfirm=[];
|
||||
me.toParse=[];
|
||||
me.toResult=[];
|
||||
me.index=null;
|
||||
refresh();
|
||||
}
|
||||
|
||||
function parseDownloads(){
|
||||
me.toParse = document.getElementById("txtInput").value.replace("\r\n","\n").split("\n");
|
||||
var pos;
|
||||
while ((pos=me.toParse.indexOf(""))>=0) {
|
||||
me.toParse.splice(pos,1);
|
||||
}
|
||||
var parser = document.createElement('a');
|
||||
me.toConfirm = [];
|
||||
me.index = 0;
|
||||
if (me.toParse.length > me.index){
|
||||
window.setTimeout("require(\"adddownloads\").parseOne()",1);
|
||||
}
|
||||
}
|
||||
|
||||
function addDownloads(){
|
||||
me.toConfirm.map(function(item){
|
||||
if (item.confirmed) {
|
||||
item.debug_msg="";
|
||||
me.toAdd.push(item.target);
|
||||
}
|
||||
});
|
||||
me.toConfirm=[];
|
||||
if (me.toAdd.length > 0){
|
||||
me.index=0;
|
||||
window.setTimeout("require(\"adddownloads\").addOne()",1);
|
||||
} else {
|
||||
cancel();
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
|
||||
function refresh(){
|
||||
m.startComputation();
|
||||
m.endComputation();
|
||||
}
|
||||
|
||||
function cancelBtn(){
|
||||
return m("div.btn2", {
|
||||
style:{
|
||||
textAlign: "center",
|
||||
color: "red",
|
||||
borderColor: "red",
|
||||
},
|
||||
onclick:cancel,
|
||||
},"cancel");
|
||||
}
|
||||
|
||||
// paste links
|
||||
function step1(){
|
||||
m.initControl = "txtInput";
|
||||
return [
|
||||
m("h3","step 1 / 5: paste retroshare-links:"),
|
||||
m("textarea[id=txtInput]", {
|
||||
style: {
|
||||
height:"100%",
|
||||
},
|
||||
onkeydown: function(event){
|
||||
if (event.keyCode == 13){
|
||||
parseDownloads();
|
||||
}
|
||||
}
|
||||
}),
|
||||
m("div.btn2", {
|
||||
style:{
|
||||
textAlign:"center",
|
||||
},
|
||||
onclick:parseDownloads,
|
||||
},"add downloads")
|
||||
]
|
||||
}
|
||||
|
||||
// parsing links
|
||||
function step2(){
|
||||
return [
|
||||
m("h3","step 2 / 5: parsing input ..."),
|
||||
m("p",
|
||||
"parsing " + (me.index) + " / " + me.toParse.length),
|
||||
cancelBtn(),
|
||||
]
|
||||
}
|
||||
|
||||
// parsing confirm
|
||||
function step3(){
|
||||
return [
|
||||
m("h3","step 3 / 5: confirm-links:"),
|
||||
m("ul",
|
||||
me.toConfirm.map(function(item){
|
||||
return m("li", {
|
||||
style:{
|
||||
color: item.confirmed
|
||||
? "lime"
|
||||
: "red"
|
||||
},
|
||||
}, item.text);
|
||||
})
|
||||
),
|
||||
m("div.btn2", {
|
||||
style:{
|
||||
textAlign:"center",
|
||||
},
|
||||
onclick:addDownloads,
|
||||
},"add green listed downloads"),
|
||||
cancelBtn(),
|
||||
]
|
||||
}
|
||||
|
||||
// adding links
|
||||
function step4(){
|
||||
return [
|
||||
m("h3","step 4 / 5: adding downloads:"),
|
||||
m("p",
|
||||
"adding " + (me.index) + " / " + me.toParse.length),
|
||||
m("ul",
|
||||
me.toAdd.map(function(item){
|
||||
return m("li", {
|
||||
style:{
|
||||
color: item.ok === undefined
|
||||
? "white"
|
||||
: item.ok == null
|
||||
? "grey"
|
||||
: item.ok
|
||||
? "lime"
|
||||
: "red"
|
||||
},
|
||||
}, (item.debug_msg ? item.debug_msg + ": " : "") + item.name
|
||||
+ " " + item.size + " " + item.hash);
|
||||
})
|
||||
),
|
||||
cancelBtn(),
|
||||
]
|
||||
}
|
||||
|
||||
// show result
|
||||
function step5(){
|
||||
return [
|
||||
m("h3","step 5 / 5: Result:"),
|
||||
m("p",
|
||||
"verarbeitet: " + me.toResult.length),
|
||||
m("ul",
|
||||
me.toResult.map(function(item){
|
||||
return m("li", {
|
||||
style:{
|
||||
color: item.ok === undefined
|
||||
? "white"
|
||||
: item.ok == null
|
||||
? "grey"
|
||||
: item.ok
|
||||
? "lime"
|
||||
: "red"
|
||||
},
|
||||
}, (item.debug_msg ? item.debug_msg + ": " : "") + item.name);
|
||||
})
|
||||
),
|
||||
m("div.btn2", {
|
||||
style:{
|
||||
textAlign: "center",
|
||||
},
|
||||
onclick: cancel,
|
||||
},"ok"),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
||||
module.exports = me;
|
54
libresapi/src/webui-src/app/addidentity.js
Normal file
54
libresapi/src/webui-src/app/addidentity.js
Normal file
@ -0,0 +1,54 @@
|
||||
"use strict";
|
||||
|
||||
var m = require("mithril");
|
||||
var rs = require("retroshare");
|
||||
|
||||
function createidentity(){
|
||||
var data = {
|
||||
name: document.getElementById("txtname").value,
|
||||
pgp_linked: false,
|
||||
//document.getElementById("chklinked").checked,
|
||||
};
|
||||
m.route("/waiting");
|
||||
rs.request("identity/create_identity",data, function(){
|
||||
m.route("/identities");
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {view: function(){
|
||||
m.initControl = "txtname";
|
||||
return m("div",
|
||||
m("h2","create identity"),
|
||||
m("hr"),
|
||||
m("h3","name"),
|
||||
m("input", {
|
||||
type: "text",
|
||||
id: "txtname",
|
||||
/*
|
||||
onkeydown: function(event){
|
||||
if (event.keyCode == 13){
|
||||
setPasswd(this.value);
|
||||
sendPassword(needpasswd);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}),
|
||||
/*
|
||||
m("b","linked with pgp-id: "),
|
||||
m("input", {
|
||||
type: "checkbox",
|
||||
id: "chklinked",
|
||||
style: {
|
||||
fontweight:"bold",
|
||||
width: "0%",
|
||||
}
|
||||
}),
|
||||
*/
|
||||
m("p"," "),
|
||||
m("input.btn2", {
|
||||
onclick: createidentity,
|
||||
type: "button",
|
||||
value: "create new identity",
|
||||
})
|
||||
)
|
||||
}}
|
151
libresapi/src/webui-src/app/addpeer.js
Normal file
151
libresapi/src/webui-src/app/addpeer.js
Normal file
@ -0,0 +1,151 @@
|
||||
"use strict";
|
||||
|
||||
var m = require("mithril");
|
||||
var rs = require("retroshare");
|
||||
|
||||
var newkey = "";
|
||||
var remote = "";
|
||||
|
||||
module.exports = {
|
||||
view: function(){
|
||||
var key = m.route.param("radix");
|
||||
var pgp = m.route.param("pgp_id");
|
||||
var peer_id =m.route.param("peer_id");
|
||||
|
||||
if (key===undefined && pgp === undefined) {
|
||||
|
||||
var owncert = rs("peers/self/certificate");
|
||||
if (owncert === undefined ) {
|
||||
owncert = {cert_string:"< waiting for server ... >"}
|
||||
}
|
||||
return m("div", [
|
||||
m("h2","add new friend (Step 1/3)"),
|
||||
m("p","Your own key, give it to your friends"),
|
||||
m("pre", owncert.cert_string),
|
||||
m("p","paste your friends key below"),
|
||||
m("textarea", {
|
||||
ref:"cert",
|
||||
cols:"70",
|
||||
rows:"16",
|
||||
onchange: m.withAttr("value", function(value){newkey=value;})
|
||||
}),
|
||||
m("br"),
|
||||
m("input.btn2",{
|
||||
type:"button",
|
||||
value:"read",
|
||||
onclick: function (){
|
||||
m.route("/addpeer",{radix:newkey})
|
||||
},
|
||||
})
|
||||
]);
|
||||
} else if (pgp === undefined) {
|
||||
rs.request("peers/examine_cert",{cert_string:key},
|
||||
function(data,responsetoken){
|
||||
data.radix=key;
|
||||
m.route("/addpeer", data);
|
||||
}
|
||||
);
|
||||
return m("div", [
|
||||
m("h2","add new friend (Step 2/3)"),
|
||||
m("div", "analyse cert, please wait for server ...")
|
||||
// { data: null, debug_msg: "failed to load certificate ", returncode: "fail" }
|
||||
]);
|
||||
} else {
|
||||
var result = {
|
||||
cert_string:key ,
|
||||
flags:{
|
||||
allow_direct_download:false,
|
||||
allow_push:false,
|
||||
// set to false, until the webinterface supports managment of the blacklist/whitelist
|
||||
require_whitelist: false,
|
||||
}
|
||||
};
|
||||
return m("div",[
|
||||
m("h2","add new friend (Step 3/3)"),
|
||||
m("p","Do you want to add "
|
||||
+ m.route.param("name")
|
||||
+ " (" + m.route.param("location") + ")"
|
||||
+ " to your friendslist?"),
|
||||
m("input.checkbox[type=checkbox]", {
|
||||
onchange: m.withAttr("checked", function(checked){
|
||||
result.flags.allow_direct_download=checked;
|
||||
})
|
||||
}), "Allow direct downloads from this node",
|
||||
m("br"),
|
||||
m("input.checkbox[type=checkbox]", {
|
||||
onchange: m.withAttr("checked", function(checked){
|
||||
result.flags.allow_push=checked;
|
||||
})
|
||||
}), "Auto download recommended files from this node",
|
||||
m("div.btn2",{
|
||||
onclick: function(){
|
||||
m.route("/waiting");
|
||||
rs.request("peers",result,function(data, responsetoken){
|
||||
m.route("/peers");
|
||||
})
|
||||
}
|
||||
},"add to friendslist")
|
||||
|
||||
])
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
return "peers/self/certificate"
|
||||
|
||||
|
||||
|
||||
RS.request({path: "peers/examine_cert", data: {cert_string: cert_string}}, this.examine_cert_callback);
|
||||
this.setState({page:"waiting", cert_string: cert_string});
|
||||
|
||||
|
||||
RS.request(
|
||||
{
|
||||
path: "peers",
|
||||
data: {
|
||||
cert_string: this.state.cert_string,
|
||||
flags:{
|
||||
allow_direct_download: this.refs.cb_direct_dl.getDOMNode().checked,
|
||||
allow_push: this.refs.cb_push.getDOMNode().checked,
|
||||
// set to false, until the webinterface supports managment of the blacklist/whitelist
|
||||
require_whitelist: false,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
render: function(){
|
||||
if(this.state.page === "start")
|
||||
return(
|
||||
<div>
|
||||
<p>Your own key, give it to your friends</p>
|
||||
<OwnCert/>
|
||||
<p>paste your friends key below</p>
|
||||
<textarea ref="cert" cols="70" rows="16"></textarea><br/>
|
||||
<input
|
||||
type="button"
|
||||
value="read key"
|
||||
onClick={this.add_friend_handler}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
if(this.state.page === "waiting")
|
||||
return(
|
||||
<div>
|
||||
waiting for response from server...
|
||||
</div>
|
||||
);
|
||||
if(this.state.page === "peer")
|
||||
return(
|
||||
<div>
|
||||
<p>Do you want to add {this.state.data.name} to your friendslist?</p>
|
||||
<input className="checkbox" type="checkbox" ref="cb_direct_dl"/> Allow direct downloads from this node<br/>
|
||||
<input className="checkbox" type="checkbox" ref="cb_push"/> Auto download recommended files from this node<br/>
|
||||
<div onClick={this.final_add_handler} className="btn2">add to friendslist</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
*/
|
23
libresapi/src/webui-src/app/assets/index.html
Normal file
23
libresapi/src/webui-src/app/assets/index.html
Normal file
@ -0,0 +1,23 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>rswebui5</title>
|
||||
<link rel="stylesheet" href="app.css">
|
||||
<script src="app.js"></script>
|
||||
</head>
|
||||
<body onload="load_ui();">
|
||||
<div id="main">if app does not load, enable JavaScript!</div>
|
||||
<script type="text/javascript">
|
||||
function load_ui(){
|
||||
var m = require("mithril");
|
||||
var ui = require("main");
|
||||
var main = document.getElementById("main");
|
||||
ui.init(main);
|
||||
if (m.initControl != undefined) {
|
||||
m.initControl.focus();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
265
libresapi/src/webui-src/app/chat.js
Normal file
265
libresapi/src/webui-src/app/chat.js
Normal file
@ -0,0 +1,265 @@
|
||||
"use strict";
|
||||
|
||||
var m = require("mithril");
|
||||
var rs = require("retroshare");
|
||||
|
||||
var msg = null;
|
||||
var particips = [];
|
||||
|
||||
function dspmsg(from, when, text){
|
||||
return m(".chat.msg.container",[
|
||||
m(".chat.msg.from", from),
|
||||
m(".chat.msg.when", when),
|
||||
m(".chat.msg.text", text),
|
||||
]);
|
||||
}
|
||||
|
||||
function lobbies(){
|
||||
return [
|
||||
rs.list("chat/lobbies",function(lobby){
|
||||
return m("div.btn",{
|
||||
title: "topic: " + lobby.topic + "\n"
|
||||
+ "subscribed: " + lobby.subscribed,
|
||||
style: {
|
||||
backgroundColor: lobby.subscribed ? 'blue' : 'darkred',
|
||||
},
|
||||
onclick: function(){
|
||||
m.route("/chat?lobby=" + lobby.chat_id);
|
||||
}
|
||||
},
|
||||
lobby.name + (
|
||||
lobby.unread_msg_count > 0
|
||||
? ("(" + lobby.unread_msg_count + ")")
|
||||
: "")
|
||||
);
|
||||
},
|
||||
rs.sort.bool("is_broadcast",
|
||||
rs.sort.bool("subscribed",
|
||||
rs.sort("name")))
|
||||
),
|
||||
m("br"),
|
||||
m("h3","peers:"),
|
||||
rs.list("peers",function(peer){
|
||||
return peer.locations.map(function(loc){
|
||||
if (loc.location == "") {
|
||||
return [];
|
||||
};
|
||||
return m("div.btn",{
|
||||
style: {
|
||||
backgroundColor: loc.is_online ? 'blue' : 'darkred',
|
||||
},
|
||||
onclick: function(){
|
||||
m.route("/chat?lobby=" + loc.chat_id);
|
||||
}
|
||||
},
|
||||
peer.name + " / " + loc.location + (
|
||||
loc.unread_msgs > 0
|
||||
? ("(" + loc.unread_msgs + ")")
|
||||
: "")
|
||||
);
|
||||
})
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
function getLobbyDetails(lobbyid){
|
||||
var lobs = rs("chat/lobbies");
|
||||
if (lobs === undefined) {
|
||||
return null;
|
||||
};
|
||||
for(var i = 0, l = lobs.length; i < l; ++i) {
|
||||
if (lobs[i].chat_id == lobbyid) {
|
||||
return lobs[i];
|
||||
}
|
||||
}
|
||||
var peers = rs("peers");
|
||||
if (peers === undefined) {
|
||||
return null;
|
||||
};
|
||||
for(var i = 0, l = peers.length; i < l; ++i) {
|
||||
var peer = peers[i];
|
||||
for(var i1 = 0, l1 = peer.locations.length; i1 < l1; ++i1) {
|
||||
if (peer.locations[i1].chat_id == lobbyid) {
|
||||
return peer.locations[i1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function sendmsg(msgid){
|
||||
var txtmsg = document.getElementById("txtNewMsg");
|
||||
rs.request("chat/send_message", {
|
||||
chat_id: msgid,
|
||||
msg: txtmsg.value
|
||||
});
|
||||
txtmsg.value="";
|
||||
}
|
||||
|
||||
function lobby(lobbyid){
|
||||
var msgs;
|
||||
var lobdt = getLobbyDetails(lobbyid);
|
||||
var info = rs("chat/info/" + lobbyid);
|
||||
if (lobdt == null || info === undefined) {
|
||||
return m("div","waiting ...");
|
||||
}
|
||||
|
||||
var mem = rs.memory("chat/info/" + lobbyid);
|
||||
if (mem.msg === undefined) {
|
||||
mem.msg = [];
|
||||
};
|
||||
|
||||
var reqData = {};
|
||||
if (mem.lastKnownMsg != undefined) {
|
||||
reqData.begin_after = mem.lastKnownMsg;
|
||||
}
|
||||
|
||||
rs.request("chat/messages/" + lobbyid, reqData, function (data) {
|
||||
mem.msg = mem.msg.concat(data);
|
||||
if (mem.msg.length > 0) {
|
||||
mem.lastKnownMsg = mem.msg[mem.msg.length -1].id;
|
||||
}
|
||||
if (data.length > 0 ) {
|
||||
rs.request("chat/mark_chat_as_read/" + lobbyid,{}, null,
|
||||
{allow: "ok|not_set"});
|
||||
}
|
||||
}, {
|
||||
onmismatch: function (){},
|
||||
log:function(){} //no logging (pulling)
|
||||
});
|
||||
|
||||
var intro = [
|
||||
m("h2",lobdt.name),
|
||||
m("p",lobdt.topic ? lobdt.topic: lobdt.location),
|
||||
m("hr")
|
||||
]
|
||||
if (lobdt.subscribed != undefined && !lobdt.subscribed) {
|
||||
return [
|
||||
intro,
|
||||
m("b","select subscribe identity:"),
|
||||
m("p"),
|
||||
rs.list("identity/own", function(item){
|
||||
return m("div.btn2",{
|
||||
onclick:function(){
|
||||
console.log("subscribe - id:" + lobdt.id +", "
|
||||
+ "gxs_id:" + item.gxs_id)
|
||||
rs.request("chat/subscribe_lobby",{
|
||||
id:lobdt.id,
|
||||
gxs_id:item.gxs_id
|
||||
})
|
||||
}
|
||||
},"subscribe as " + item.name);
|
||||
}),
|
||||
];
|
||||
} else {
|
||||
msg = m(".chat.bottom",[
|
||||
m("div","enter new message:"),
|
||||
m("input",{
|
||||
id:"txtNewMsg",
|
||||
onkeydown: function(event){
|
||||
if (event.keyCode == 13){
|
||||
sendmsg(lobbyid);
|
||||
}
|
||||
}
|
||||
}),
|
||||
m("div.btn2", {
|
||||
style: {textAlign:"center"},
|
||||
onclick: function(){
|
||||
sendmsg(lobbyid);
|
||||
}
|
||||
},"submit")
|
||||
]);
|
||||
}
|
||||
if (lobdt.subscribed != undefined
|
||||
&& lobdt.subscribed
|
||||
&& !lobdt.is_broadcast
|
||||
) {
|
||||
//set participants
|
||||
particips = [
|
||||
m("div.btn", {
|
||||
style: {
|
||||
"text-align":"center"
|
||||
},
|
||||
onclick: function (){
|
||||
rs.request("chat/unsubscribe_lobby",{
|
||||
id:lobdt.id,
|
||||
});
|
||||
m.route("/chat");
|
||||
}
|
||||
},"unsubscribe"),
|
||||
m("h3","participants:"),
|
||||
rs.list(
|
||||
"chat/lobby_participants/" + lobbyid,
|
||||
function(item) {
|
||||
return m("div",item.identity.name);
|
||||
},
|
||||
function (a,b){
|
||||
return rs.stringSort(a.identity.name,b.identity.name);
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
return [
|
||||
intro,
|
||||
mem.msg.map(function(item){
|
||||
var d = new Date(new Number(item.send_time)*1000);
|
||||
return dspmsg(
|
||||
item.author_name,
|
||||
d.toLocaleDateString() + " " + d.toLocaleTimeString(),
|
||||
item.msg
|
||||
);
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
frame: function(content, right){
|
||||
return m("div", {
|
||||
style: {
|
||||
"height": "100%",
|
||||
"box-sizing": "border-box",
|
||||
"padding-bottom": "170px",
|
||||
}
|
||||
},[
|
||||
m(".chat.container", [
|
||||
m(".chat.header", [
|
||||
m(
|
||||
"h2",
|
||||
{style:{margin:"0px"}},
|
||||
"chat"
|
||||
)
|
||||
]),
|
||||
m(".chat.left", [
|
||||
m("div.chat.header[style=position:relative]","lobbies:"),
|
||||
m("br"),
|
||||
lobbies(),
|
||||
]),
|
||||
m(".chat.right", right),
|
||||
m(".chat.middle", content),
|
||||
m(".chat.clear", ""),
|
||||
]),
|
||||
msg != null
|
||||
? msg
|
||||
: [],
|
||||
]);
|
||||
},
|
||||
view: function(){
|
||||
var lobbyid = m.route.param("lobby");
|
||||
msg = null;
|
||||
if (lobbyid != undefined ) {
|
||||
particips = [];
|
||||
return this.frame(
|
||||
lobby(lobbyid),
|
||||
particips
|
||||
);
|
||||
};
|
||||
return this.frame(
|
||||
m(
|
||||
"div",
|
||||
{style: {margin:"10px"}},
|
||||
"please select lobby"
|
||||
),
|
||||
m("div","")
|
||||
);
|
||||
}
|
||||
}
|
254
libresapi/src/webui-src/app/createlogin.js
Normal file
254
libresapi/src/webui-src/app/createlogin.js
Normal file
@ -0,0 +1,254 @@
|
||||
var m = require("mithril");
|
||||
var rs = require("retroshare");
|
||||
|
||||
var locationName = "";
|
||||
var password ="";
|
||||
var ssl_name = "";
|
||||
var newName = "";
|
||||
|
||||
function listprofiles(){
|
||||
var locations = rs("control/locations");
|
||||
var knownProfileIds = [];
|
||||
var result = [];
|
||||
if(locations === undefined || locations == null){
|
||||
return m("div", "profiles: waiting_server");
|
||||
}
|
||||
locations.map(function(location) {
|
||||
if (knownProfileIds.indexOf(location.pgp_id)<0){
|
||||
knownProfileIds.push(location.pgp_id);
|
||||
result.push(m(
|
||||
"div.btn2",{
|
||||
onclick: function(){
|
||||
m.route("/createlogin",{
|
||||
id: location.pgp_id,
|
||||
name: location.name,
|
||||
})
|
||||
}
|
||||
},
|
||||
location.name
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function setLocationName(location) {
|
||||
locationName = location;
|
||||
}
|
||||
|
||||
function setPasswd(passwd) {
|
||||
password = passwd;
|
||||
}
|
||||
|
||||
function setSslName(ssl) {
|
||||
ssl_name = ssl;
|
||||
}
|
||||
|
||||
function setNewName(name) {
|
||||
newName = name;
|
||||
}
|
||||
|
||||
function checkpasswd(){
|
||||
var status = "";
|
||||
var color = "red";
|
||||
var lbl = document.getElementById("lblpwdinfo");
|
||||
var passwd2 = document.getElementById("txtpasswd2").value;
|
||||
if (passwd2 == password && passwd2 != "") {
|
||||
color = "lime";
|
||||
status = "password ok";
|
||||
} else if (passwd2 == "") {
|
||||
color = "yellow";
|
||||
status = "password required";
|
||||
} else {
|
||||
color = "red";
|
||||
status = "passwords don't match";
|
||||
}
|
||||
lbl.textContent = status;
|
||||
lbl.style.color=color;
|
||||
}
|
||||
|
||||
|
||||
function createLocation() {
|
||||
var profile = m.route.param("id");
|
||||
var profname = m.route.param("name");
|
||||
|
||||
var loc ={
|
||||
ssl_name: document.getElementById("txtlocation").value,
|
||||
pgp_password: password,
|
||||
};
|
||||
if (profile != undefined) {
|
||||
loc.pgp_id= profile;
|
||||
} else {
|
||||
loc.pgp_name = newName;
|
||||
};
|
||||
rs.request("control/create_location",loc,function(data){
|
||||
m.route("/accountselect", {});
|
||||
});
|
||||
m.route("/createlogin",{state:wait});
|
||||
}
|
||||
|
||||
function certDrop(event)
|
||||
{
|
||||
console.log("onDrop()");
|
||||
console.log(event.dataTransfer.files);
|
||||
event.preventDefault();
|
||||
|
||||
var reader = new FileReader();
|
||||
|
||||
var widget = this;
|
||||
|
||||
reader.onload = function(evt) {
|
||||
console.log("onDrop(): file loaded");
|
||||
rs.request(
|
||||
"control/import_pgp",{
|
||||
key_string:evt.target.result,
|
||||
}, importCallback);
|
||||
};
|
||||
reader.readAsText(event.dataTransfer.files[0]);
|
||||
m.route("/createlogin",{state:"waiting"});
|
||||
}
|
||||
|
||||
function importCallback(resp)
|
||||
{
|
||||
console.log("importCallback()" + resp);
|
||||
m.route("/createlogin",{
|
||||
id:resp.pgp_id,
|
||||
name:"",
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
view: function(){
|
||||
var profile = m.route.param("id");
|
||||
var state = m.route.param("state");
|
||||
var profname = m.route.param("name");
|
||||
var hidden = m.route.param("hidden");
|
||||
if (state == "wait"){
|
||||
return m("div","waiting ...");
|
||||
} if (state == "newid"){
|
||||
m.initControl = "txtnewname";
|
||||
return m("div",[
|
||||
m("h2","create login - Step 2 / 2: create location"),
|
||||
m("h3","- for new profile "),
|
||||
m("hr"),
|
||||
m("h2","PGP-profile name:"),
|
||||
m("input",{
|
||||
id: "txtnewname",
|
||||
type:"text",
|
||||
onchange:m.withAttr("value", setNewName),
|
||||
onkeydown: function(event){
|
||||
if (event.keyCode == 13){
|
||||
document.getElementById("txtpasswd").focus();
|
||||
}
|
||||
},
|
||||
}),
|
||||
m("h2","enter password:"),
|
||||
m("input", {
|
||||
id: "txtpasswd",
|
||||
type:"password",
|
||||
onchange: m.withAttr("value",setPasswd),
|
||||
onkeydown: function(event){
|
||||
if (event.keyCode == 13){
|
||||
setPasswd(this.value);
|
||||
document.getElementById("txtpasswd2").focus();
|
||||
};
|
||||
checkpasswd;
|
||||
}
|
||||
}),
|
||||
m("h2", "re-enter password:"),
|
||||
m("input", {
|
||||
id: "txtpasswd2",
|
||||
type:"password",
|
||||
onfocus: checkpasswd,
|
||||
onchange: checkpasswd,
|
||||
onkeyup: function(event){
|
||||
if (event.keyCode == 13){
|
||||
document.getElementById("txtlocation").focus();
|
||||
}
|
||||
checkpasswd();
|
||||
}
|
||||
}),
|
||||
m("h3",{
|
||||
id: "lblpwdinfo",
|
||||
style:"color:yellow",
|
||||
}, "password required"),
|
||||
m("h2","location name:"),
|
||||
m("input",{
|
||||
id: "txtlocation",
|
||||
type:"text",
|
||||
onchange:m.withAttr("value", setLocationName),
|
||||
onkeydown: function(event){
|
||||
if (event.keyCode == 13){
|
||||
setSslName(this.value);
|
||||
createLocation();
|
||||
}
|
||||
},
|
||||
}),
|
||||
m("br"),
|
||||
m("input",{
|
||||
type: "button",
|
||||
onclick: createLocation,
|
||||
value: "create location",
|
||||
}),
|
||||
]);
|
||||
} else if (profile != undefined) {
|
||||
m.initControl = "txtpasswd";
|
||||
return m("div",[
|
||||
m("h2","create login - Step 2 / 2: create location"),
|
||||
m("h3","- for " + profname + " (" +profile + ")"),
|
||||
m("hr"),
|
||||
m("h2","enter password:"),
|
||||
m("input", {
|
||||
id: "txtpasswd",
|
||||
type:"password",
|
||||
onchange: m.withAttr("value",setPasswd),
|
||||
onkeydown: function(event){
|
||||
if (event.keyCode == 13){
|
||||
setPasswd(this.value);
|
||||
document.getElementById("txtlocation").focus();
|
||||
}
|
||||
}
|
||||
}),
|
||||
m("h2","location name:"),
|
||||
m("input",{
|
||||
id: "txtlocation",
|
||||
type:"text",
|
||||
onchange:m.withAttr("value", setLocationName),
|
||||
onkeydown: function(event){
|
||||
if (event.keyCode == 13){
|
||||
setSslName(this.value);
|
||||
createLocation();
|
||||
}
|
||||
},
|
||||
}),
|
||||
m("br"),
|
||||
m("input",{
|
||||
type: "button",
|
||||
onclick: createLocation,
|
||||
value: "create location",
|
||||
}),
|
||||
]);
|
||||
} else {
|
||||
return m("div",[
|
||||
m("h2","create login - Step 1 / 2: select profile(PGP-ID)"),
|
||||
m("hr"),
|
||||
m("div.btn2",{
|
||||
onclick: function(){
|
||||
m.route("/createlogin", {state: "newid"});
|
||||
},
|
||||
} ,"<create new profile>"),
|
||||
m("div.btn2",{
|
||||
ondragover:function(event){
|
||||
/*important: block default event*/
|
||||
event.preventDefault();
|
||||
},
|
||||
ondrop: certDrop,
|
||||
} ,"<import profile (drag and drop a profile here)>"),
|
||||
listprofiles()
|
||||
]);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
96
libresapi/src/webui-src/app/downloads.js
Normal file
96
libresapi/src/webui-src/app/downloads.js
Normal file
@ -0,0 +1,96 @@
|
||||
var m = require("mithril");
|
||||
var rs = require("retroshare");
|
||||
|
||||
function makeFriendlyUnit(bytes)
|
||||
{
|
||||
if(bytes < 1e3)
|
||||
return bytes.toFixed(1) + "B";
|
||||
if(bytes < 1e6)
|
||||
return (bytes/1e3).toFixed(1) + "kB";
|
||||
if(bytes < 1e9)
|
||||
return (bytes/1e6).toFixed(1) + "MB";
|
||||
if(bytes < 1e12)
|
||||
return (bytes/1e9).toFixed(1) + "GB";
|
||||
return (bytes/1e12).toFixed(1) + "TB";
|
||||
}
|
||||
|
||||
function progressBar(file){
|
||||
return m("div[style=border:5px solid lime;"
|
||||
+ 'border-radius:3mm;'
|
||||
+ 'padding:2mm;'
|
||||
+ 'height:5mm'
|
||||
+ "]", [
|
||||
m("div[style="
|
||||
+ 'background-color:lime;'
|
||||
+ 'height:100%;'
|
||||
+ 'width:' + (file.transfered / file.size * 100)+'%'
|
||||
+ ']'
|
||||
,"")
|
||||
]);
|
||||
};
|
||||
|
||||
function cntrlBtn(file, act) {
|
||||
return(
|
||||
m("div.btn",{
|
||||
onclick: function(){
|
||||
rs.request("transfers/control_download",{action: act, id: file.id});
|
||||
}
|
||||
},
|
||||
act)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
view: function(){
|
||||
var paths = rs("transfers/downloads");
|
||||
var filestreamer_url = "/fstream/";
|
||||
if (paths === undefined) {
|
||||
return m("div", "Downloads ... please wait ...");
|
||||
}
|
||||
return m("div", [
|
||||
m("h2","Downloads (" + paths.length +")"),
|
||||
m("div.btn2", {
|
||||
onclick: function(){
|
||||
m.route("/downloads/add");
|
||||
}
|
||||
}, "add retrohare downloads"),
|
||||
m("hr"),
|
||||
m('table', [
|
||||
m("tr",[
|
||||
m("th","name"),
|
||||
m("th","size"),
|
||||
m("th","progress"),
|
||||
m("th","transfer rate"),
|
||||
m("th","status"),
|
||||
m("th","progress"),
|
||||
m("th","action")
|
||||
]),
|
||||
paths.map(function (file){
|
||||
var ctrlBtn = m("div","");
|
||||
var progress = file.transfered / file.size * 100;
|
||||
return m("tr",[
|
||||
m("td",[
|
||||
m("a.filelink",
|
||||
{
|
||||
target: "blank",
|
||||
href: filestreamer_url + file.hash + "/" + encodeURIComponent(file.name)
|
||||
},
|
||||
file.name
|
||||
)
|
||||
]),
|
||||
m("td", makeFriendlyUnit(file.size)),
|
||||
m("td", progress.toPrecision(3) + "%"),
|
||||
m("td", makeFriendlyUnit(file.transfer_rate*1e3)+"/s"),
|
||||
m("td", file.download_status),
|
||||
m("td", progressBar(file)),
|
||||
m("td", [
|
||||
cntrlBtn(file, file.download_status==="paused"?"start":"pause"),
|
||||
cntrlBtn(file, "cancel")]
|
||||
)
|
||||
])
|
||||
})
|
||||
])
|
||||
]);
|
||||
}
|
||||
};
|
65
libresapi/src/webui-src/app/forums.js
Normal file
65
libresapi/src/webui-src/app/forums.js
Normal file
@ -0,0 +1,65 @@
|
||||
"use strict";
|
||||
|
||||
var m = require("mithril");
|
||||
var rs = require("retroshare");
|
||||
|
||||
module.exports = {view: function(){
|
||||
return m("div",[
|
||||
m("h2","forums"),
|
||||
m("p","(work in progress, currently only listing)"),
|
||||
m("hr"),
|
||||
/*
|
||||
m("div.btn2", {
|
||||
onclick: function (){
|
||||
m.route("/addforum");
|
||||
}
|
||||
},"< create new forum >"),
|
||||
*/
|
||||
m("ul",
|
||||
rs.list("forums",
|
||||
function(item){
|
||||
return m("li",[
|
||||
m("h2",item.name),
|
||||
m("div",{style:{margin:"10px"}},
|
||||
[
|
||||
item.description != ""
|
||||
? [
|
||||
m("span", "Description: "
|
||||
+ item.description),
|
||||
m("br")]
|
||||
: [],
|
||||
m("span","messages visible: "
|
||||
+ item.visible_msg_count),
|
||||
]
|
||||
),
|
||||
/*
|
||||
item.subscribed
|
||||
? [
|
||||
m(
|
||||
"span.btn2",
|
||||
{style:{padding:"0px"}},
|
||||
"unsubscribe"
|
||||
),
|
||||
" ",
|
||||
m(
|
||||
"span.btn2",
|
||||
{style:{padding:"0px", margin:"10px"}},
|
||||
"enter"
|
||||
),
|
||||
m("hr", {style: {color:"silver"}}),
|
||||
]
|
||||
: [
|
||||
m(
|
||||
"span.btn2",
|
||||
{style:{padding:"0px", margin:"10px"}},
|
||||
"subscribe"
|
||||
),
|
||||
]
|
||||
*/
|
||||
]);
|
||||
},
|
||||
rs.sort("name")
|
||||
)
|
||||
)
|
||||
]);
|
||||
}}
|
145
libresapi/src/webui-src/app/green-black.scss
Normal file
145
libresapi/src/webui-src/app/green-black.scss
Normal file
@ -0,0 +1,145 @@
|
||||
body {
|
||||
background-color: black;
|
||||
color: lime;
|
||||
font-family: monospace;
|
||||
margin: 0em;
|
||||
/*padding: 1.5em;*/
|
||||
padding: 2mm;
|
||||
font-size: 1.1em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#overlay{
|
||||
z-index: 10;
|
||||
position: fixed;
|
||||
top:0;
|
||||
left:0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0,0,0,0.8);
|
||||
}
|
||||
|
||||
.paddingbox{
|
||||
padding:2mm;
|
||||
}
|
||||
|
||||
.nav{
|
||||
list-style-type: none;
|
||||
padding: 0em;
|
||||
margin: 0em;
|
||||
}
|
||||
|
||||
.nav li{
|
||||
display: inline;
|
||||
padding: 0.1em;
|
||||
margin-right: 1em;
|
||||
border-width: 0.1em;
|
||||
border-color: blue;
|
||||
border-bottom-style: solid;
|
||||
cursor: pointer;
|
||||
}
|
||||
td{
|
||||
padding: 0.3em;
|
||||
border-style: solid;
|
||||
border-width: 0.1em;
|
||||
border-color: lime;
|
||||
}
|
||||
hr {
|
||||
color: lime;
|
||||
}
|
||||
.menu{
|
||||
border-style: solid;
|
||||
border-color: lime;
|
||||
border-width: 0.1em;
|
||||
cursor: pointer;
|
||||
padding: 0.0em;
|
||||
}
|
||||
.btn{
|
||||
border-style: solid;
|
||||
border-color: lime;
|
||||
border-width: 0.1em;
|
||||
cursor: pointer;
|
||||
padding: 0.1em;
|
||||
}
|
||||
|
||||
.btn2, .box{
|
||||
border-style: solid;
|
||||
/*border-color: lime;*/
|
||||
border-color: limeGreen;
|
||||
/*border-width: 1px;*/
|
||||
border-radius: 3mm;
|
||||
padding: 2mm;
|
||||
font-size: 10mm;
|
||||
cursor: pointer;
|
||||
margin-bottom: 2mm;
|
||||
}
|
||||
.btn2:hover{
|
||||
background-color: midnightblue;
|
||||
}
|
||||
|
||||
.filelink{
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
input, textarea{
|
||||
color: lime;
|
||||
font-family: monospace;
|
||||
background-color: black;
|
||||
border-color: lime;
|
||||
font-size: 10mm;
|
||||
border-radius: 3mm;
|
||||
border-width: 1mm;
|
||||
padding: 2mm;
|
||||
margin-bottom: 2mm;
|
||||
margin-right: 2mm;
|
||||
|
||||
/* make the button the whole screen width */
|
||||
width: 100%;
|
||||
/* make the text input fit small screens*/
|
||||
box-sizing: border-box;
|
||||
}
|
||||
input:hover{
|
||||
background-color: midnightblue;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.flexbox{
|
||||
display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */
|
||||
display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */
|
||||
display: -ms-flexbox; /* TWEENER - IE 10 */
|
||||
display: -webkit-flex; /* NEW - Chrome */
|
||||
display: flex; /* NEW, Spec - Opera 12.1, Firefox 20+ */
|
||||
}
|
||||
|
||||
.flexwidemember{
|
||||
-webkit-box-flex: 1; /* OLD - iOS 6-, Safari 3.1-6 */
|
||||
-moz-box-flex: 1; /* OLD - Firefox 19- */
|
||||
width: 20%; /* For old syntax, otherwise collapses. */
|
||||
-webkit-flex: 1; /* Chrome */
|
||||
-ms-flex: 1; /* IE 10 */
|
||||
flex: 1; /* NEW, Spec - Opera 12.1, Firefox 20+ */
|
||||
}
|
||||
|
||||
#logo_splash{
|
||||
-webkit-animation-fill-mode: forwards; /* Chrome, Safari, Opera */
|
||||
animation-fill-mode: forwards;
|
||||
-webkit-animation-name: logo_splash; /* Chrome, Safari, Opera */
|
||||
-webkit-animation-duration: 3s; /* Chrome, Safari, Opera */
|
||||
animation-name: logo_splash;
|
||||
animation-duration: 3s;
|
||||
text-align: center;
|
||||
}
|
||||
/* Chrome, Safari, Opera */
|
||||
@-webkit-keyframes logo_splash {
|
||||
from {opacity: 0;}
|
||||
to {opacity: 1;}
|
||||
}
|
||||
|
||||
/* Standard syntax */
|
||||
@keyframes logo_splash {
|
||||
from {opacity: 0;}
|
||||
to {opacity: 1;}
|
||||
}
|
6
libresapi/src/webui-src/app/home.js
Normal file
6
libresapi/src/webui-src/app/home.js
Normal file
@ -0,0 +1,6 @@
|
||||
var m = require("mithril");
|
||||
|
||||
module.exports = {view: function(){
|
||||
return m("div","RetroShare - WebClient - Welcome");
|
||||
}
|
||||
};
|
22
libresapi/src/webui-src/app/identities.js
Normal file
22
libresapi/src/webui-src/app/identities.js
Normal file
@ -0,0 +1,22 @@
|
||||
"use strict";
|
||||
|
||||
var m = require("mithril");
|
||||
var rs = require("retroshare");
|
||||
|
||||
module.exports = {view: function(){
|
||||
return m("div",[
|
||||
m("h2","identities"),
|
||||
m("hr"),
|
||||
m("div.btn2", {
|
||||
onclick: function (){
|
||||
m.route("/addidentity");
|
||||
}
|
||||
},"< create new identity >"),
|
||||
m("ul",
|
||||
rs.list("identity/own", function(item){
|
||||
return m("li",[m("h2",item.name)]);
|
||||
},
|
||||
rs.sort("name"))
|
||||
)
|
||||
]);
|
||||
}}
|
122
libresapi/src/webui-src/app/main.js
Normal file
122
libresapi/src/webui-src/app/main.js
Normal file
@ -0,0 +1,122 @@
|
||||
"use strict";
|
||||
|
||||
var m = require("mithril");
|
||||
var rs = require("retroshare");
|
||||
var menu =require("menu");
|
||||
var currentpasswd = null;
|
||||
|
||||
|
||||
function setPasswd(password) {
|
||||
currentpasswd = password
|
||||
}
|
||||
|
||||
function sendPassword(data) {
|
||||
console.log("sending pwd for " + data.key_name + "...");
|
||||
rs.request("control/password", {password: currentpasswd}, function(){
|
||||
m.redraw();
|
||||
});
|
||||
}
|
||||
|
||||
function Page(menu){
|
||||
this.menu = menu;
|
||||
this.module = (menu.module != undefined) ? menu.module : menu.name;
|
||||
this.path = menu.path != undefined ? menu.path : ("/" + menu.name);
|
||||
|
||||
var runst = menu.runstate;
|
||||
var content = require(this.module);
|
||||
var mm = require("menu");
|
||||
|
||||
this.view = function(){
|
||||
var runstate = rs("control/runstate");
|
||||
var needpasswd = rs("control/password");
|
||||
//console.log("runstate: " + (runstate === undefined ? runstate : runstate.runstate));
|
||||
if(runstate === undefined){
|
||||
return m("h2", "waiting_server ... ");
|
||||
} else if (runstate.runstate == null){
|
||||
// try clean reboot ...
|
||||
rs.clearCache();
|
||||
rs("control/runstate"); //reboot detector
|
||||
console.log("i'm down");
|
||||
return m("h2", "server down");
|
||||
} else if (needpasswd != undefined && needpasswd.want_password === true){
|
||||
m.initControl = "txtpasswd";
|
||||
return m("div",[
|
||||
m("h2","password required"),
|
||||
m("h3",needpasswd.key_name),
|
||||
m("input",{
|
||||
id: "txtpasswd",
|
||||
type:"password",
|
||||
onchange:m.withAttr("value", setPasswd),
|
||||
onkeydown: function(event){
|
||||
if (event.keyCode == 13){
|
||||
setPasswd(this.value);
|
||||
sendPassword(needpasswd);
|
||||
}
|
||||
}
|
||||
}),
|
||||
m("br"),
|
||||
m("input[type=button][value=send password]",{
|
||||
onclick: function(){
|
||||
sendPassword(needpasswd);
|
||||
}
|
||||
}),
|
||||
]);
|
||||
} else {
|
||||
if ("waiting_init|waiting_startup".match(runstate.runstate)) {
|
||||
return m("h2","server starting ...")
|
||||
} else if("waiting_account_select|running_ok.*".match(runstate.runstate)) {
|
||||
if (runst === undefined || runst.match(runstate.runstate)) {
|
||||
return m("div", {
|
||||
style: {
|
||||
height: "100%",
|
||||
"box-sizing": "border-box",
|
||||
"padding-bottom": "40px"
|
||||
}
|
||||
}, [
|
||||
m("div", mm.view()),
|
||||
m("hr"),
|
||||
m("div", {
|
||||
style: {
|
||||
height: "100%",
|
||||
"box-sizing": "border-box",
|
||||
"padding-bottom":"40px"
|
||||
}
|
||||
}, content)
|
||||
]);
|
||||
} else {
|
||||
// funktion currently not available
|
||||
m.route("/");
|
||||
return m("div", [
|
||||
m("div", mm.view()),
|
||||
m("hr"),
|
||||
m("div", require("home").view())
|
||||
]);
|
||||
};
|
||||
} else {
|
||||
return m("div", "unknown runstate: " + runstate.runstate);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
init:function(main){
|
||||
console.log("start init ...");
|
||||
var menudef = require("menudef");
|
||||
var maps = {};
|
||||
var m = require("mithril");
|
||||
|
||||
menudef.nodes.map(function(menu){
|
||||
if (menu.action === undefined) {
|
||||
var p = new Page(menu)
|
||||
console.log("adding route " + menu.name + " for " + p.path + " with module " + p.module);
|
||||
maps[p.path] = p;
|
||||
}
|
||||
});
|
||||
m.route.mode = "hash";
|
||||
m.route(main,"/",maps);
|
||||
console.log("init done.");
|
||||
}
|
||||
};
|
||||
|
12
libresapi/src/webui-src/app/main.sass
Normal file
12
libresapi/src/webui-src/app/main.sass
Normal file
@ -0,0 +1,12 @@
|
||||
/*@import "reset" */
|
||||
|
||||
html, body, #main
|
||||
height: 100%
|
||||
|
||||
|
||||
/*body */
|
||||
/* font-family: "Sans-serif" */
|
||||
|
||||
|
||||
@import "chat"
|
||||
@import "green-black"
|
62
libresapi/src/webui-src/app/menu.js
Normal file
62
libresapi/src/webui-src/app/menu.js
Normal file
@ -0,0 +1,62 @@
|
||||
"use strict";
|
||||
|
||||
var m = require("mithril");
|
||||
var rs = require("retroshare");
|
||||
var mnodes = require("menudef");
|
||||
|
||||
function goback(){
|
||||
rs.content=null;
|
||||
m.redraw();
|
||||
}
|
||||
|
||||
function buildmenu(menu, tagname, runstate, ignore){
|
||||
if (
|
||||
(menu.runstate === undefined || runstate.match(menu.runstate)!=null)
|
||||
&& (!ignore.match(menu.name))
|
||||
&& (menu.path === undefined || menu.path.match(":")==null)
|
||||
&& (menu.show === undefined || menu.show)
|
||||
) {
|
||||
var name = menu.name;
|
||||
if (menu.counter != undefined) {
|
||||
name += menu.counter();
|
||||
}
|
||||
if (menu.action === undefined) {
|
||||
return m(tagname , {
|
||||
onclick: function(){
|
||||
m.route(
|
||||
menu.path != undefined ? menu.path : "/" + menu.name
|
||||
)
|
||||
}
|
||||
}, name);
|
||||
} else {
|
||||
return m(tagname, {onclick: function(){menu.action(m)}}, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {view: function(){
|
||||
var runstate = rs("control/runstate");
|
||||
if (runstate === undefined || runstate.runstate === undefined || runstate.runstate == null)
|
||||
return m("div.nav","menu: waiting for server ...");
|
||||
if (m.route() != "/")
|
||||
return m("span",[
|
||||
m("span"," | "),
|
||||
mnodes.nodes.map(function(menu){
|
||||
var item = buildmenu(menu,"span.menu", runstate.runstate, "");
|
||||
if (item != null){
|
||||
return [
|
||||
item,
|
||||
m("span"," | ")
|
||||
]
|
||||
}
|
||||
})
|
||||
]);
|
||||
return m("div", [
|
||||
m("h2","home"),
|
||||
m("hr"),
|
||||
mnodes.nodes.map(function(menu){
|
||||
return buildmenu(menu,"div.btn2", runstate.runstate, "home");
|
||||
})
|
||||
]);
|
||||
}
|
||||
};
|
119
libresapi/src/webui-src/app/menudef.js
Normal file
119
libresapi/src/webui-src/app/menudef.js
Normal file
@ -0,0 +1,119 @@
|
||||
var rs=require("retroshare");
|
||||
module.exports = { nodes: [
|
||||
{
|
||||
name: "home",
|
||||
path: "/"
|
||||
},
|
||||
{
|
||||
name: "login",
|
||||
module: "accountselect",
|
||||
runstate: "waiting_account_select",
|
||||
counter: rs.counting("control/locations"),
|
||||
},
|
||||
{
|
||||
name: "create login",
|
||||
path: "/createlogin",
|
||||
module: "createlogin",
|
||||
runstate: "waiting_account_select",
|
||||
},
|
||||
{
|
||||
name: "peers",
|
||||
runstate: "running_ok.*",
|
||||
counter: rs.counting("peers", function(data){
|
||||
var onlinecount = 0;
|
||||
data.map(function(peer) {
|
||||
var is_online = false;
|
||||
peer.locations.map(function (location){
|
||||
if (location.is_online) {
|
||||
is_online=true;
|
||||
}
|
||||
});
|
||||
if (is_online) {
|
||||
onlinecount +=1;
|
||||
}
|
||||
});
|
||||
return onlinecount + "/" + data.length;
|
||||
})
|
||||
},
|
||||
{
|
||||
name: "addpeer",
|
||||
runstate: "running_ok.*",
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
name: "identities",
|
||||
runstate: "running_ok.*",
|
||||
counter: rs.counting("identity/own"),
|
||||
},
|
||||
{
|
||||
name: "addidentity",
|
||||
runstate: "running_ok.*",
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
name:"searchresult",
|
||||
path: "/search/:id",
|
||||
runstate: "running_ok.*",
|
||||
},
|
||||
{
|
||||
name: "search",
|
||||
runstate: "running_ok.*",
|
||||
},
|
||||
{
|
||||
name: "downloads",
|
||||
runstate: "running_ok.*",
|
||||
counter: rs.counting("transfers/downloads")
|
||||
},
|
||||
{
|
||||
name: "adddownloads",
|
||||
runstate: "running_ok.*",
|
||||
path: "/downloads/add",
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
name: "forums",
|
||||
runstate: "running_ok.*",
|
||||
},
|
||||
{
|
||||
name: "chat",
|
||||
runstate: "running_ok.*",
|
||||
counter: rs.counting2({
|
||||
"peers": function(peer) {
|
||||
var sum = 0;
|
||||
peer.locations.map(function (loc) {
|
||||
sum += parseInt(loc.unread_msgs);
|
||||
});
|
||||
return sum;
|
||||
},
|
||||
"chat/lobbies": function(lobby) {
|
||||
return lobby.unread_msg_count;
|
||||
}
|
||||
})
|
||||
},
|
||||
{
|
||||
name:"settings",
|
||||
runstate: "running_ok.*",
|
||||
},
|
||||
{
|
||||
name:"servicecontrol",
|
||||
runstate: "running_ok.*",
|
||||
path:"/settings/servicecontrol",
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
name: "shutdown",
|
||||
runstate: "running_ok|waiting_account_select",
|
||||
action: function(m){
|
||||
rs.request("control/shutdown",null,function(){
|
||||
rs("control/runstate").runstate=null;
|
||||
rs.forceUpdate("control/runstate");
|
||||
m.redraw();
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "waiting",
|
||||
show: false,
|
||||
},
|
||||
]
|
||||
}
|
2141
libresapi/src/webui-src/app/mithril.js
Normal file
2141
libresapi/src/webui-src/app/mithril.js
Normal file
File diff suppressed because it is too large
Load Diff
8
libresapi/src/webui-src/app/mithril.min.js
vendored
Normal file
8
libresapi/src/webui-src/app/mithril.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
libresapi/src/webui-src/app/mithril.min.js.map
Normal file
1
libresapi/src/webui-src/app/mithril.min.js.map
Normal file
File diff suppressed because one or more lines are too long
89
libresapi/src/webui-src/app/peers.js
Normal file
89
libresapi/src/webui-src/app/peers.js
Normal file
@ -0,0 +1,89 @@
|
||||
"use strict";
|
||||
|
||||
var m = require("mithril");
|
||||
var rs = require("retroshare");
|
||||
|
||||
module.exports = {view: function(){
|
||||
var peers = rs("peers");
|
||||
//console.log("peers:" + peers);
|
||||
|
||||
//waiting for peerlist ...
|
||||
if(peers === undefined || peers == null){
|
||||
return m("div",[
|
||||
m("h2","peers"),
|
||||
m("h3","waiting_server"),
|
||||
]);
|
||||
};
|
||||
peers = peers.sort(rs.sort("name"));
|
||||
|
||||
//building peerlist (prebuild for counting)
|
||||
var online = 0;
|
||||
var peerlist = peers.map(function(peer){
|
||||
var isonline = false;
|
||||
var avatar_address ="";
|
||||
|
||||
//building location list (prebuild for state + icon)
|
||||
var loclist = peer.locations.map(function(location){
|
||||
if (location.is_online && ! isonline){
|
||||
online +=1;
|
||||
isonline = true;
|
||||
}
|
||||
if (location.avatar_address != "" && avatar_address =="") {
|
||||
avatar_address=location.avatar_address;
|
||||
}
|
||||
return m("li",{
|
||||
style:"color:" + (location.is_online ? "lime": "grey")
|
||||
+ ";cursor:pointer",
|
||||
onclick: function(){
|
||||
m.route("/chat?lobby=" + location.chat_id)
|
||||
}
|
||||
|
||||
}, location.location);
|
||||
});
|
||||
|
||||
//return friend (peer + locations)
|
||||
return m("div.flexbox[style=color:lime]",[
|
||||
// avatar-icon
|
||||
m("div", [
|
||||
avatar_address == "" ? "" : (
|
||||
m("img",{
|
||||
src: rs.apiurl("peers" + avatar_address),
|
||||
style:"border-radius:3mm;margin:2mm;",
|
||||
})
|
||||
)
|
||||
]),
|
||||
//peername + locations
|
||||
m("div.flexwidemember",[
|
||||
m("h1[style=margin-bottom:1mm;]",
|
||||
{style:"color:" + (isonline ? "lime": "grey")} ,
|
||||
peer.name
|
||||
),
|
||||
m("ul", loclist ),
|
||||
]),
|
||||
//remove-button
|
||||
m("div", {
|
||||
style: "color:red;" +
|
||||
"font-size:1.5em;" +
|
||||
"padding:0.2em;" +
|
||||
"cursor:pointer",
|
||||
onclick: function (){
|
||||
var yes = window.confirm(
|
||||
"Remove " + peer.name + " from friendslist?");
|
||||
if(yes){
|
||||
rs.request("peers/" + peer.pgp_id +"/delete");
|
||||
}
|
||||
}
|
||||
}, "X")
|
||||
]);
|
||||
});
|
||||
|
||||
// return add-peer-button + peerlist
|
||||
return m("div",[
|
||||
m("div.btn2",{onclick: function(){m.route("/addpeer")}},"add new friend"),
|
||||
m("h2","peers (online: " + online +" / " + peers.length + "):"),
|
||||
m("div", [
|
||||
peerlist,
|
||||
]),
|
||||
]);
|
||||
}
|
||||
};
|
410
libresapi/src/webui-src/app/retroshare.js
Normal file
410
libresapi/src/webui-src/app/retroshare.js
Normal file
@ -0,0 +1,410 @@
|
||||
/*
|
||||
var rs = requires("rs");
|
||||
var m = require("mithril");
|
||||
|
||||
function main(){
|
||||
var state = rs("runstate");
|
||||
if(state=== undefined){
|
||||
return m("div", "waiting for server");
|
||||
}
|
||||
if(state === "waiting_login"){
|
||||
return require("login")();
|
||||
}
|
||||
if(state === "running_ok"){
|
||||
return require("mainwindow")();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
idea: statetokenservice could just send the date instead of the token
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var m = require("mithril");
|
||||
|
||||
var api_url = window.location.protocol + "//" + window.location.hostname + ":" + window.location.port + "/api/v2/";
|
||||
var filestreamer_url = window.location.protocol + "//" +window.location.hostname + ":" + window.location.port + "/fstream/";
|
||||
var upload_url = window.location.protocol + "//" + window.location.hostname + ":" + window.location.port + "/upload/";
|
||||
|
||||
function for_key_in_obj(obj, callback){
|
||||
var key;
|
||||
for(key in obj){
|
||||
callback(key, obj[key]);
|
||||
}
|
||||
}
|
||||
|
||||
var cache = {};
|
||||
var last_update_ts = 0;
|
||||
|
||||
function check_for_changes(){
|
||||
var tokens = [];
|
||||
var paths_to_fetch = [];
|
||||
// console.log("start-check " + Object.keys(cache));
|
||||
for_key_in_obj(cache, function(path, item){
|
||||
var token = item.statetoken;
|
||||
if(token === undefined || token== null) {
|
||||
paths_to_fetch.push(path)
|
||||
} else if (tokens.indexOf(token)<0) {
|
||||
tokens.push(token);
|
||||
}
|
||||
});
|
||||
// console.log("tokens found: " + tokens);
|
||||
var req = m.request({
|
||||
method: "POST",
|
||||
url: api_url + "statetokenservice",
|
||||
background: true,
|
||||
data: tokens,
|
||||
});
|
||||
|
||||
req.then(function handle_statetoken_response(response){
|
||||
// console.log("checking result " + response.data ? Object.keys(response.data) : "<null>") ;
|
||||
for_key_in_obj(cache, function(path, item){
|
||||
var found = false;
|
||||
for(var i = 0; i < response.data.length; i++){
|
||||
if(response.data[i] === item.statetoken){
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if(found){
|
||||
paths_to_fetch.push(path);
|
||||
}
|
||||
});
|
||||
// console.log("generating Results for paths " + paths_to_fetch);
|
||||
var requests = [];
|
||||
paths_to_fetch.map(function request_it(path){
|
||||
var req2 = m.request({
|
||||
method: "GET",
|
||||
url: api_url + path,
|
||||
background: true,
|
||||
});
|
||||
req2 = req2.then(function fill_in_result(response){
|
||||
cache[path].data = response.data;
|
||||
cache[path].statetoken = response.statetoken;
|
||||
});
|
||||
requests.push(req2);
|
||||
});
|
||||
if(requests.length > 0){
|
||||
// console.log("requesting " + requests.length + " requests");
|
||||
m.sync(requests).then(function trigger_render(){
|
||||
m.startComputation();
|
||||
m.endComputation();
|
||||
checkFocus();
|
||||
setTimeout(check_for_changes, 500);
|
||||
});
|
||||
}
|
||||
else{
|
||||
// console.log("no requests");
|
||||
setTimeout(check_for_changes, 500);
|
||||
}
|
||||
}, function errhandling(value){
|
||||
// console.log("server disconnected " + value);
|
||||
setTimeout(check_for_changes, 500);
|
||||
});
|
||||
}
|
||||
|
||||
check_for_changes();
|
||||
|
||||
var update_scheduled = false;
|
||||
function schedule_request_missing(){
|
||||
if(update_scheduled)
|
||||
return;
|
||||
update_scheduled = true;
|
||||
// place update logic outside of render loop, this way we can fetch multiple things at once
|
||||
// (because after the render loop everything we should fetch is in the list)
|
||||
// if we fetch multiple things at once, we can delay a re-rende runtil everything is done
|
||||
// so we need only one re-render for multiple updates
|
||||
setTimeout(function request_missing(){
|
||||
update_scheduled = false;
|
||||
var requests = [];
|
||||
for_key_in_obj(cache, function(path, item){
|
||||
if(!item.requested){
|
||||
var req = m.request({
|
||||
method: "GET",
|
||||
url: api_url + path,
|
||||
background: true,
|
||||
});
|
||||
|
||||
req.then(function fill_data(response){
|
||||
// TODO: add errorhandling
|
||||
item.data = response.data;
|
||||
item.statetoken = response.statetoken;
|
||||
if (item.then != undefined && item.then != null) {
|
||||
try {
|
||||
item.then(response);
|
||||
} catch (ex) {
|
||||
if (item.errorCallback != undefined && item.errorCallback != null) {
|
||||
item.errorCallback(ex);
|
||||
};
|
||||
}
|
||||
};
|
||||
}, function errhandling(value){
|
||||
if (item.errorCallback != undefined && item.errorCallback != null) {
|
||||
item.errorCallback(value);
|
||||
}
|
||||
});
|
||||
requests.push(req);
|
||||
}
|
||||
item.requested = true;
|
||||
});
|
||||
m.sync(requests).then(function trigger_render(){
|
||||
m.startComputation();
|
||||
m.endComputation();
|
||||
checkFocus();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function checkFocus(){
|
||||
if (m.initControl != undefined) {
|
||||
var ctrl = document.getElementById(m.initControl);
|
||||
if (ctrl!= null) {
|
||||
ctrl.focus();
|
||||
m.initControl = undefined;
|
||||
} else {
|
||||
console.log("focus-control '" + m.initControl + "' not found!");
|
||||
m.initControl = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// called every time, rs or rs.request failed, only response or value is set
|
||||
function requestFail(path, response, value) {
|
||||
rs.error = "error on " + path;
|
||||
console.log("Error on " + path +
|
||||
(response == null ? ", value: " + value : (", response: " +
|
||||
(response.debug_msg === undefined ? response : response.debug_msg)
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
function rs(path, args, callback, options){
|
||||
if(cache[path] === undefined){
|
||||
options=optionsPrep(options,path);
|
||||
var req = {
|
||||
data: args,
|
||||
statetoken: undefined,
|
||||
requested: false,
|
||||
allow: options.allow,
|
||||
then: function(response){
|
||||
options.log(path + ": response: " + response.returncode);
|
||||
if (!this.allow.match(response.returncode)) {
|
||||
options.onmismatch(response);
|
||||
} else if (callback != undefined && callback != null) {
|
||||
callback(response.data, response.statetoken);
|
||||
}
|
||||
},
|
||||
errorCallback: options.onfail
|
||||
};
|
||||
cache[path] = req;
|
||||
schedule_request_missing();
|
||||
}
|
||||
return cache[path].data;
|
||||
}
|
||||
|
||||
module.exports = rs;
|
||||
|
||||
rs.for_key_in_obj = for_key_in_obj;
|
||||
|
||||
// single request for action
|
||||
rs.request=function(path, args, callback, options){
|
||||
options = optionsPrep(options, path);
|
||||
var req = m.request({
|
||||
method: options.method === undefined ? "POST" : options.method,
|
||||
url: api_url + path,
|
||||
data: args,
|
||||
background: true
|
||||
});
|
||||
req.then(function checkResponseAndCallback(response){
|
||||
options.log(path + ": response: " + response.returncode);
|
||||
if (!options.allow.match(response.returncode)) {
|
||||
options.onmismatch(response);
|
||||
} else if (callback != undefined && callback != null) {
|
||||
callback(response.data, response.statetoken);
|
||||
}
|
||||
}, options.onfail);
|
||||
return req;
|
||||
};
|
||||
|
||||
//set default-values for shared options in rs() and rs.request()
|
||||
function optionsPrep(options, path) {
|
||||
if (options === undefined) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
if (options.onfail === undefined) {
|
||||
options.onfail = function errhandling(value){
|
||||
requestFail(path, null, value);
|
||||
}
|
||||
};
|
||||
if (options.onmismatch === undefined) {
|
||||
options.onmismatch = function errhandling(response){
|
||||
requestFail(path, response,null);
|
||||
}
|
||||
};
|
||||
|
||||
if (options.log === undefined) {
|
||||
options.log = function(message) {
|
||||
console.log(message);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.allow === undefined) {
|
||||
options.allow = "ok";
|
||||
};
|
||||
return options;
|
||||
}
|
||||
|
||||
// force reload for path
|
||||
rs.forceUpdate = function(path, removeCache){
|
||||
if (removeCache === undefined || !removeCache) {
|
||||
cache[path].requested=false;
|
||||
} else {
|
||||
delete cache[path];
|
||||
}
|
||||
}
|
||||
|
||||
// force reload for all
|
||||
rs.clearCache = function(path, removeCache){
|
||||
console.log("clearing Cache ...")
|
||||
cache = {};
|
||||
console.log("update_scheduled: " + update_scheduled);
|
||||
update_scheduled = false;
|
||||
check_for_changes();
|
||||
console.log("Cache cleared.")
|
||||
}
|
||||
|
||||
// dismiss statetoken (= force reload)
|
||||
rs.untoken = function(path) {
|
||||
cache[path].statetoken = null;
|
||||
}
|
||||
|
||||
|
||||
//return api-path
|
||||
rs.apiurl = function(path) {
|
||||
if (path === undefined) {
|
||||
path="";
|
||||
}
|
||||
if (path.length > 0 && "^\\\\|\\/".match(path)) {
|
||||
path=path.substr(1);
|
||||
}
|
||||
return api_url + path;
|
||||
}
|
||||
|
||||
// counting in menu
|
||||
rs.counting = function(path, counterfnkt) {
|
||||
return function () {
|
||||
var data=rs(path);
|
||||
if (data != undefined) {
|
||||
if (counterfnkt === undefined) {
|
||||
return " (" + data.length + ")";
|
||||
}
|
||||
return " (" + counterfnkt(data) + ")";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
// counting in menu
|
||||
rs.counting2 = function(targets) {
|
||||
return function () {
|
||||
var sum = 0;
|
||||
for (var path in targets) {
|
||||
var data=rs(path);
|
||||
if (data != undefined) {
|
||||
data.map(function(item){
|
||||
sum += parseInt(targets[path](item));
|
||||
});
|
||||
};
|
||||
};
|
||||
if (sum > 0) {
|
||||
return " (" + sum + ")";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
// listing data-elements
|
||||
rs.list = function(path, buildfktn, sortfktn){
|
||||
var list = rs(path);
|
||||
if (list === undefined|| list == null) {
|
||||
return "< waiting for server ... >"
|
||||
};
|
||||
if (sortfktn != undefined && sortfktn != null) {
|
||||
list=list.sort(sortfktn);
|
||||
}
|
||||
return list.map(buildfktn);
|
||||
};
|
||||
|
||||
//remember additional data (feature of last resort)
|
||||
rs.memory = function(path, args){
|
||||
var item = cache[path];
|
||||
if (item === undefined) {
|
||||
rs(path, args);
|
||||
item = cache[path];
|
||||
}
|
||||
if (item.memory === undefined) {
|
||||
item.memory = {};
|
||||
}
|
||||
return item.memory;
|
||||
};
|
||||
|
||||
// Sortierfunktion für Texte von Objekten,
|
||||
// falls einfache Namen nicht funktionieren
|
||||
rs.stringSort = function(textA,textB, innersort, objectA, objectB){
|
||||
if (textA.toLowerCase() == textB.toLowerCase()) {
|
||||
if (innersort === undefined) {
|
||||
return 0
|
||||
}
|
||||
return innersort(objectA,objectB);
|
||||
} else if (textA.toLowerCase() < textB.toLowerCase()) {
|
||||
return -1
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//return sorting-function for string, based on property name
|
||||
//using: list.sort(rs.sort("name"));
|
||||
// -----
|
||||
//innersort: cascading sorting - using:
|
||||
//list.sort(rs.sort("type",rs.sort("name")))
|
||||
rs.sort = function(name, innersort){
|
||||
return function(a,b) {
|
||||
return rs.stringSort(a[name],b[name],innersort,a,b);
|
||||
}
|
||||
}
|
||||
|
||||
//return sorting-function for boolean, based on property name
|
||||
rs.sort.bool = function(name, innersort){
|
||||
return function(a,b){
|
||||
if (a[name] == b[name]) {
|
||||
if (innersort === undefined) {
|
||||
return 0
|
||||
}
|
||||
return innersort(a,b);
|
||||
} else if (a[name]) {
|
||||
return -1
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// searching a element in a list
|
||||
// items: list to search in
|
||||
// name: name of attribute to lookup
|
||||
// value: attribute's value to compare
|
||||
rs.find = function(items, name, value) {
|
||||
if (items === undefined||items == null) {
|
||||
return null;
|
||||
};
|
||||
for(var i = 0, l = items.length; i < l; ++i) {
|
||||
if (items[i][name] == value) {
|
||||
return items[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
54
libresapi/src/webui-src/app/search.js
Normal file
54
libresapi/src/webui-src/app/search.js
Normal file
@ -0,0 +1,54 @@
|
||||
var m = require("mithril");
|
||||
var rs = require("retroshare");
|
||||
|
||||
var state = {};
|
||||
var searchText = "";
|
||||
|
||||
function updateText(newText) {
|
||||
searchText = newText;
|
||||
}
|
||||
|
||||
function dosearch(){
|
||||
console.log("searching for: "+searchText);
|
||||
rs.request(
|
||||
"filesearch/create_search", {
|
||||
distant: true,
|
||||
search_string: searchText
|
||||
},
|
||||
function(resp){
|
||||
m.route("/search/" + resp.search_id);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
view: function(){
|
||||
var results = rs("filesearch");
|
||||
if (results === undefined||results == null) {
|
||||
results = [];
|
||||
};
|
||||
return m("div",[
|
||||
m("h2","turtle file search"),
|
||||
m("div", [
|
||||
m("input[type=text]", {onchange:m.withAttr("value", updateText)}),
|
||||
m("input[type=button][value=search]",{onclick:dosearch})
|
||||
]),
|
||||
m("hr"),
|
||||
m("h2","previous searches:"),
|
||||
m("div", [
|
||||
results.map(function(item){
|
||||
var res = rs("filesearch/" + item.id,{},null,{allow:"not_set|ok"});
|
||||
if (res === undefined) {
|
||||
res =[];
|
||||
};
|
||||
return m("div.btn2",{
|
||||
onclick:function(){
|
||||
m.route("/search/" + item.id);
|
||||
}
|
||||
}, item.search_string + " (" + res.length + ")");
|
||||
})
|
||||
])
|
||||
])
|
||||
}
|
||||
}
|
||||
|
71
libresapi/src/webui-src/app/searchresult.js
Normal file
71
libresapi/src/webui-src/app/searchresult.js
Normal file
@ -0,0 +1,71 @@
|
||||
var m = require("mithril");
|
||||
var rs = require("retroshare");
|
||||
|
||||
module.exports = {
|
||||
view: function(){
|
||||
var id=m.route.param("id");
|
||||
var results = rs("filesearch/" + id ,{},null,{allow:"not_set|ok"});
|
||||
if (results === undefined || results.length == undefined) {
|
||||
results = [];
|
||||
}
|
||||
var searches = rs("filesearch");
|
||||
var searchdetail = "<unknown>";
|
||||
if (!(searches === undefined) && !(searches.length === undefined)) {
|
||||
searches.forEach(function(s){
|
||||
if (s.id == id) {
|
||||
searchdetail = s.search_string;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var dl_ids = [];
|
||||
|
||||
var downloads =rs("transfers/downloads");
|
||||
if (downloads !== undefined) {
|
||||
downloads.map(function(item){
|
||||
dl_ids.push(item.hash);
|
||||
})
|
||||
}
|
||||
|
||||
return m("div",[
|
||||
m("h2","turtle file search results"),
|
||||
m("h3", "searchtext: " + searchdetail + " (" + results.length + ")"),
|
||||
m("hr"),
|
||||
m("table", [
|
||||
m("tr" ,[
|
||||
m("th","name"),
|
||||
m("th","size"),
|
||||
m("th",""),
|
||||
]),
|
||||
results.map(function(file){
|
||||
if (dl_ids.indexOf(file.hash)>=0) {
|
||||
file.state="in download queue"
|
||||
}
|
||||
return m("tr",[
|
||||
m("th",file.name),
|
||||
m("th",file.size),
|
||||
m("th",[
|
||||
file.state === undefined
|
||||
? m("span.btn", {
|
||||
onclick:function(){
|
||||
rs.request("transfers/control_download", {
|
||||
action: "begin",
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
hash: file.hash,
|
||||
}, function(){
|
||||
result="added";
|
||||
});
|
||||
m.startComputation();
|
||||
m.endComputation();
|
||||
}
|
||||
}, "download")
|
||||
: file.state
|
||||
]),
|
||||
])
|
||||
})
|
||||
])
|
||||
])
|
||||
}
|
||||
}
|
||||
|
264
libresapi/src/webui-src/app/servicecontrol.js
Normal file
264
libresapi/src/webui-src/app/servicecontrol.js
Normal file
@ -0,0 +1,264 @@
|
||||
"use strict";
|
||||
|
||||
var m = require("mithril");
|
||||
var rs = require("retroshare");
|
||||
|
||||
function setOption(id,value) {
|
||||
return function(){
|
||||
rs.request("servicecontrol", {
|
||||
service_id: id,
|
||||
default_allowed: value,
|
||||
});
|
||||
rs.forceUpdate("servicecontrol", true);
|
||||
}
|
||||
}
|
||||
|
||||
function setUserOption(serviceid, userid, value) {
|
||||
return function(){
|
||||
rs.request("servicecontrol/user", {
|
||||
service_id: serviceid,
|
||||
peer_id: userid,
|
||||
enabled: value
|
||||
}, function(){
|
||||
rs.forceUpdate("servicecontrol", true)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function createSwitch(isOn, width) {
|
||||
if (width === undefined) {
|
||||
width = "2.1em";
|
||||
}
|
||||
return [
|
||||
m("div.menu", {
|
||||
style: {
|
||||
float:"left",
|
||||
width: width,
|
||||
textAlign: "center",
|
||||
color: "#303030",
|
||||
borderColor: isOn
|
||||
? "lime"
|
||||
: "red",
|
||||
backgroundColor: !isOn
|
||||
? "black"
|
||||
: "lime",
|
||||
}
|
||||
}, "ON"),
|
||||
m("div.menu",{
|
||||
style: {
|
||||
float:"left",
|
||||
width: width,
|
||||
textAlign: "center",
|
||||
marginRight:"5px",
|
||||
color: "#303030",
|
||||
borderColor: isOn
|
||||
? "lime"
|
||||
: "red",
|
||||
backgroundColor: isOn
|
||||
? "black"
|
||||
: "red",
|
||||
}
|
||||
}, "OFF"),
|
||||
];
|
||||
}
|
||||
|
||||
function breadcrums(name, parts){
|
||||
var result = [];
|
||||
rs.for_key_in_obj(parts, function(partname,item){
|
||||
result.push(
|
||||
m("span.btn",{
|
||||
onclick: function(){
|
||||
m.route(item)
|
||||
}
|
||||
},partname)
|
||||
);
|
||||
result.push(" / ");
|
||||
});
|
||||
result.push(name);
|
||||
return result;
|
||||
}
|
||||
|
||||
function serviceView(serviceid) {
|
||||
var service, liste;
|
||||
service = rs.find(rs("servicecontrol"),"service_id",serviceid);
|
||||
if (service == null) {
|
||||
return m("h3","<please wait ... >");
|
||||
}
|
||||
liste = service.default_allowed
|
||||
? service.peers_denied
|
||||
: service.peers_allowed;
|
||||
return m("div", [
|
||||
m("h2", breadcrums(service.service_name, {
|
||||
settings:"/settings",
|
||||
rights: "/settings/servicecontrol",
|
||||
})),
|
||||
m("hr"),
|
||||
m("h2",{
|
||||
style:{
|
||||
float:"left",
|
||||
}
|
||||
},[
|
||||
m("div",{
|
||||
style:{
|
||||
float:"left",
|
||||
}
|
||||
},"user rights for: " + service.service_name + ", default: "),
|
||||
m("div", {
|
||||
onclick: setOption(
|
||||
serviceid,
|
||||
!service.default_allowed
|
||||
),
|
||||
style: {
|
||||
float:"left",
|
||||
marginLeft: "0.4em",
|
||||
marginRight: "0.4em",
|
||||
}
|
||||
},createSwitch(service.default_allowed)),
|
||||
]),
|
||||
m("div", {
|
||||
style: {
|
||||
clear:"left",
|
||||
}
|
||||
}),
|
||||
m("ul", rs.list("peers",function(peer){
|
||||
var locs;
|
||||
locs = peer.locations;
|
||||
locs.sort(rs.sort("location"));
|
||||
return peer.locations.map(function(location){
|
||||
var isExcept, isOn;
|
||||
isExcept = liste != null
|
||||
&& liste.indexOf(location.peer_id)>=0;
|
||||
isOn = service.default_allowed ? !isExcept: isExcept;
|
||||
return m("li", {
|
||||
style: {
|
||||
margin: "5px",
|
||||
color: isOn ? "lime" :"red",
|
||||
}
|
||||
}, [
|
||||
m("div"),
|
||||
m("div", {
|
||||
onclick: setUserOption(
|
||||
serviceid,
|
||||
location.peer_id,
|
||||
!isOn
|
||||
),
|
||||
style: {
|
||||
float:"left",
|
||||
},
|
||||
},createSwitch(isOn)),
|
||||
m("div",
|
||||
{
|
||||
style: {
|
||||
//color: "lime",
|
||||
float:"left",
|
||||
marginLeft: "5px",
|
||||
marginRight: "5px",
|
||||
fontWeight: "bold",
|
||||
}
|
||||
},
|
||||
peer.name + (location.location
|
||||
? " (" + location.location + ")"
|
||||
: "")
|
||||
),
|
||||
m("div", {
|
||||
style: {
|
||||
clear: "left"
|
||||
}
|
||||
}),
|
||||
]);
|
||||
})
|
||||
}, rs.sort("name")))
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
view: function(){
|
||||
if (m.route.param("service_id")) {
|
||||
return serviceView(m.route.param("service_id"));
|
||||
}
|
||||
return m("div", [
|
||||
m("h2", breadcrums("rights", {
|
||||
settings:"/settings",
|
||||
})),
|
||||
m("hr"),
|
||||
m("ul", rs.list("servicecontrol", function(item){
|
||||
return m("li", {
|
||||
style: {
|
||||
margin: "5px",
|
||||
color: item.default_allowed ? "lime" :"red",
|
||||
}
|
||||
}, [
|
||||
m("div"),
|
||||
m("div", {
|
||||
onclick: setOption(
|
||||
item.service_id,
|
||||
!item.default_allowed
|
||||
),
|
||||
style: {
|
||||
float:"left",
|
||||
}
|
||||
},createSwitch(item.default_allowed)),
|
||||
m("div.menu",
|
||||
{
|
||||
style: {
|
||||
// color: "lime",
|
||||
borderColor: item.default_allowed
|
||||
? "lime"
|
||||
: "red",
|
||||
float: "left",
|
||||
marginLeft: "5px",
|
||||
marginRight: "5px",
|
||||
paddingLeft: "2px",
|
||||
paddingRight: "2px",
|
||||
},
|
||||
onclick: function(){
|
||||
m.route("/settings/servicecontrol/", {
|
||||
service_id: item.service_id,
|
||||
})
|
||||
}
|
||||
}, "more"
|
||||
),
|
||||
m("div",
|
||||
{
|
||||
style: {
|
||||
// color: "lime",
|
||||
float:"left",
|
||||
marginLeft: "5px",
|
||||
marginRight: "5px",
|
||||
fontWeight: "bold",
|
||||
}
|
||||
},
|
||||
item.service_name
|
||||
),
|
||||
m("div",
|
||||
{
|
||||
style: {
|
||||
color: "lime",
|
||||
float:"left",
|
||||
marginLeft: "5px",
|
||||
marginRight: "5px",
|
||||
}
|
||||
},
|
||||
(
|
||||
item.default_allowed
|
||||
? ( item.peers_denied != null
|
||||
? "(" + item.peers_denied.length + " denied)"
|
||||
: "")
|
||||
: ( item.peers_allowed != null
|
||||
? "(" + item.peers_allowed.length + " allowed)"
|
||||
: "")
|
||||
)
|
||||
),
|
||||
m("div", {
|
||||
style: {
|
||||
clear: "left"
|
||||
}
|
||||
}),
|
||||
]);
|
||||
})
|
||||
)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
19
libresapi/src/webui-src/app/settings.js
Normal file
19
libresapi/src/webui-src/app/settings.js
Normal file
@ -0,0 +1,19 @@
|
||||
"use strict";
|
||||
|
||||
var m = require("mithril");
|
||||
var rs = require("retroshare");
|
||||
|
||||
module.exports = {
|
||||
view: function(){
|
||||
return m("div", [
|
||||
m("h2","settings"),
|
||||
m("hr"),
|
||||
m("div.btn2",{
|
||||
onclick: function(){
|
||||
m.route("/settings/servicecontrol");
|
||||
},
|
||||
}, "rights")
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
8
libresapi/src/webui-src/app/waiting.js
Normal file
8
libresapi/src/webui-src/app/waiting.js
Normal file
@ -0,0 +1,8 @@
|
||||
"use strict";
|
||||
|
||||
var m = require("mithril");
|
||||
var rs = require("retroshare");
|
||||
|
||||
module.exports = {view: function(){
|
||||
return m("h2","please wait ...");
|
||||
}}
|
12
libresapi/src/webui-src/brunch-config.js
Normal file
12
libresapi/src/webui-src/brunch-config.js
Normal file
@ -0,0 +1,12 @@
|
||||
module.exports = {
|
||||
config:{
|
||||
files:{
|
||||
javascripts:{
|
||||
joinTo: 'app.js'
|
||||
},
|
||||
stylesheets:{
|
||||
joinTo: 'app.css'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
41
libresapi/src/webui-src/make-src/build.bat
Normal file
41
libresapi/src/webui-src/make-src/build.bat
Normal file
@ -0,0 +1,41 @@
|
||||
@echo off
|
||||
REM create webfiles from sources at compile time (works without npm/node.js)
|
||||
|
||||
set publicdest=%1\webui
|
||||
set src=%1\webui-src
|
||||
|
||||
if "%1" == "" set publicdest=..\..\webui&&set src=..
|
||||
|
||||
if exist "%publicdest%" echo remove existing %publicdest%&&rd %publicdest% /S /Q
|
||||
|
||||
echo mkdir %publicdest%
|
||||
md %publicdest%
|
||||
|
||||
echo building app.js
|
||||
echo - copy template.js ...
|
||||
copy %src%\make-src\template.js %publicdest%\app.js
|
||||
|
||||
for %%F in (%src%\app\*.js) DO (set "fname=%%~nF" && CALL :addfile)
|
||||
|
||||
echo building app.css
|
||||
type %src%\app\green-black.scss >> %publicdest%\app.css
|
||||
type %src%\make-src\main.css >> %publicdest%\app.css
|
||||
type %src%\make-src\chat.css >> %publicdest%\app.css
|
||||
|
||||
echo copy index.html
|
||||
copy %src%\app\assets\index.html %publicdest%\index.html
|
||||
|
||||
echo build.bat complete
|
||||
|
||||
goto :EOF
|
||||
|
||||
:addfile
|
||||
echo - adding %fname% ...
|
||||
echo require.register("%fname%", function(exports, require, module) { >> %publicdest%\app.js
|
||||
echo %src%\app\%fname%.js
|
||||
type %src%\app\%fname%.js >> %publicdest%\app.js
|
||||
echo. >> %publicdest%\app.js
|
||||
echo }); >> %publicdest%\app.js
|
||||
|
||||
|
||||
:EOF
|
43
libresapi/src/webui-src/make-src/build.sh
Executable file
43
libresapi/src/webui-src/make-src/build.sh
Executable file
@ -0,0 +1,43 @@
|
||||
#!/usr/bin/sh
|
||||
|
||||
# create webfiles from sources at compile time (works without npm/node.js)
|
||||
|
||||
if [ "$1" == "" ];then
|
||||
publicdest=../../webui
|
||||
src=..
|
||||
else
|
||||
publicdest=$1/webui
|
||||
src=$1/webui-src
|
||||
fi
|
||||
|
||||
if [ -d "$publicdest" ]; then
|
||||
echo remove existing $publicdest
|
||||
rm $publicdest -R
|
||||
fi
|
||||
|
||||
echo mkdir $publicdest
|
||||
mkdir $publicdest
|
||||
|
||||
echo building app.js
|
||||
echo - copy template.js ...
|
||||
cp $src/make-src/template.js $publicdest/app.js
|
||||
|
||||
for filename in $src/app/*.js; do
|
||||
fname=$(basename "$filename")
|
||||
fname="${fname%.*}"
|
||||
echo - adding $fname ...
|
||||
echo require.register\(\"$fname\", function\(exports, require, module\) { >> $publicdest/app.js
|
||||
cat $filename >> $publicdest/app.js
|
||||
echo >> $publicdest/app.js
|
||||
echo }\)\; >> $publicdest/app.js
|
||||
done
|
||||
|
||||
echo building app.css
|
||||
cat $src/app/green-black.scss >> $publicdest/app.css
|
||||
cat $src/make-src/main.css >> $publicdest/app.css
|
||||
cat $src/make-src/chat.css >> $publicdest/app.css
|
||||
|
||||
echo copy index.html
|
||||
cp $src/app/assets/index.html $publicdest/index.html
|
||||
|
||||
echo build.sh complete
|
72
libresapi/src/webui-src/make-src/chat.css
Normal file
72
libresapi/src/webui-src/make-src/chat.css
Normal file
@ -0,0 +1,72 @@
|
||||
.chat {
|
||||
padding: 15px; }
|
||||
.chat.container {
|
||||
height: 100%;
|
||||
padding: 0px;
|
||||
position: relative;
|
||||
box-sizing: border-box; }
|
||||
.chat.header {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
height: 50px;
|
||||
background-color: black;
|
||||
border-bottom: solid 1px gray;
|
||||
box-sizing: border-box; }
|
||||
.chat.left {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
width: 200px;
|
||||
box-sizing: border-box;
|
||||
background-color: black; }
|
||||
.chat.right {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
width: 200px;
|
||||
box-sizing: border-box; }
|
||||
.chat.middle {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
margin-top: 50px;
|
||||
left: 200px;
|
||||
right: 200px;
|
||||
box-sizing: border-box;
|
||||
padding: 0px;
|
||||
height: 100%;
|
||||
overflow-y: scroll; }
|
||||
.chat.bottom {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
right: 200px;
|
||||
left: 200px;
|
||||
padding: 5px; }
|
||||
.chat.msg {
|
||||
padding: 0px; }
|
||||
.chat.msg.container {
|
||||
position: relative;
|
||||
border-bottom: solid 1px lightgray;
|
||||
padding: 10px;
|
||||
height: unset; }
|
||||
.chat.msg.from {
|
||||
position: absolute;
|
||||
width: 100px;
|
||||
top: 10px;
|
||||
left: 0px;
|
||||
color: white;
|
||||
text-align: right; }
|
||||
.chat.msg.when {
|
||||
float: right;
|
||||
color: lightgray;
|
||||
margin-bottom: 10px; }
|
||||
.chat.msg.text {
|
||||
padding-left: 100px;
|
||||
top: 0px;
|
||||
left: 100px;
|
||||
white-space: pre-wrap;
|
||||
height: initial; }
|
||||
|
15
libresapi/src/webui-src/make-src/init.bat
Normal file
15
libresapi/src/webui-src/make-src/init.bat
Normal file
@ -0,0 +1,15 @@
|
||||
@echo off
|
||||
REM create dummy webfiles at qmake run
|
||||
|
||||
set publicdest=%1\webui
|
||||
if "%1" == "" set publicdest=..\..\webui
|
||||
|
||||
if exist %publicdest% echo remove %publicdest%&&rd %publicdest% /S /Q
|
||||
|
||||
echo create %publicdest%
|
||||
md %publicdest%
|
||||
|
||||
echo create %publicdest%\app.js, %publicdest%\app.css, %publicdest%\index.html
|
||||
echo. > %publicdest%\app.js
|
||||
echo. > %publicdest%\app.css
|
||||
echo. > %publicdest%\index.html
|
22
libresapi/src/webui-src/make-src/init.sh
Executable file
22
libresapi/src/webui-src/make-src/init.sh
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/sh
|
||||
|
||||
# create dummy webfiles at qmake run
|
||||
|
||||
if [ "$1" == "" ];then
|
||||
publicdest=../../webui
|
||||
else
|
||||
publicdest=$1/webui
|
||||
fi
|
||||
|
||||
if [ -d "$publicdest" ]; then
|
||||
echo remove $publicdest
|
||||
rm $publicdest -R
|
||||
fi
|
||||
|
||||
echo create $publicdest
|
||||
mkdir $publicdest
|
||||
|
||||
echo touch $publicdest/app.js, $publicdest/app.css, $publicdest/index.html
|
||||
touch $publicdest/app.js
|
||||
touch $publicdest/app.css
|
||||
touch $publicdest/index.html
|
2
libresapi/src/webui-src/make-src/main.css
Normal file
2
libresapi/src/webui-src/make-src/main.css
Normal file
@ -0,0 +1,2 @@
|
||||
html, body, #main {
|
||||
height: 100%; }
|
7
libresapi/src/webui-src/make-src/readme.md
Normal file
7
libresapi/src/webui-src/make-src/readme.md
Normal file
@ -0,0 +1,7 @@
|
||||
this folder contains files needed to create webfiles at compile-time and qmake
|
||||
|
||||
* init.sh creates dummy files at qmake
|
||||
* build.sh creates files at compile time
|
||||
* chat.css compiled version of _chat.sass (.sass should replaced by .scss)
|
||||
* main.css simple template extracted from main.sass
|
||||
* template.js start of compiled app.js, containing additional created content
|
112
libresapi/src/webui-src/make-src/template.js
Normal file
112
libresapi/src/webui-src/make-src/template.js
Normal file
@ -0,0 +1,112 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var globals = typeof window === 'undefined' ? global : window;
|
||||
if (typeof globals.require === 'function') return;
|
||||
|
||||
var modules = {};
|
||||
var cache = {};
|
||||
var aliases = {};
|
||||
var has = ({}).hasOwnProperty;
|
||||
|
||||
var endsWith = function(str, suffix) {
|
||||
return str.indexOf(suffix, str.length - suffix.length) !== -1;
|
||||
};
|
||||
|
||||
var _cmp = 'components/';
|
||||
var unalias = function(alias, loaderPath) {
|
||||
var start = 0;
|
||||
if (loaderPath) {
|
||||
if (loaderPath.indexOf(_cmp) === 0) {
|
||||
start = _cmp.length;
|
||||
}
|
||||
if (loaderPath.indexOf('/', start) > 0) {
|
||||
loaderPath = loaderPath.substring(start, loaderPath.indexOf('/', start));
|
||||
}
|
||||
}
|
||||
var result = aliases[alias + '/index.js'] || aliases[loaderPath + '/deps/' + alias + '/index.js'];
|
||||
if (result) {
|
||||
return _cmp + result.substring(0, result.length - '.js'.length);
|
||||
}
|
||||
return alias;
|
||||
};
|
||||
|
||||
var _reg = /^\.\.?(\/|$)/;
|
||||
var expand = function(root, name) {
|
||||
var results = [], part;
|
||||
var parts = (_reg.test(name) ? root + '/' + name : name).split('/');
|
||||
for (var i = 0, length = parts.length; i < length; i++) {
|
||||
part = parts[i];
|
||||
if (part === '..') {
|
||||
results.pop();
|
||||
} else if (part !== '.' && part !== '') {
|
||||
results.push(part);
|
||||
}
|
||||
}
|
||||
return results.join('/');
|
||||
};
|
||||
|
||||
var dirname = function(path) {
|
||||
return path.split('/').slice(0, -1).join('/');
|
||||
};
|
||||
|
||||
var localRequire = function(path) {
|
||||
return function expanded(name) {
|
||||
var absolute = expand(dirname(path), name);
|
||||
return globals.require(absolute, path);
|
||||
};
|
||||
};
|
||||
|
||||
var initModule = function(name, definition) {
|
||||
var module = {id: name, exports: {}};
|
||||
cache[name] = module;
|
||||
definition(module.exports, localRequire(name), module);
|
||||
return module.exports;
|
||||
};
|
||||
|
||||
var require = function(name, loaderPath) {
|
||||
var path = expand(name, '.');
|
||||
if (loaderPath == null) loaderPath = '/';
|
||||
path = unalias(name, loaderPath);
|
||||
|
||||
if (has.call(cache, path)) return cache[path].exports;
|
||||
if (has.call(modules, path)) return initModule(path, modules[path]);
|
||||
|
||||
var dirIndex = expand(path, './index');
|
||||
if (has.call(cache, dirIndex)) return cache[dirIndex].exports;
|
||||
if (has.call(modules, dirIndex)) return initModule(dirIndex, modules[dirIndex]);
|
||||
|
||||
throw new Error('Cannot find module "' + name + '" from '+ '"' + loaderPath + '"');
|
||||
};
|
||||
|
||||
require.alias = function(from, to) {
|
||||
aliases[to] = from;
|
||||
};
|
||||
|
||||
require.register = require.define = function(bundle, fn) {
|
||||
if (typeof bundle === 'object') {
|
||||
for (var key in bundle) {
|
||||
if (has.call(bundle, key)) {
|
||||
modules[key] = bundle[key];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
modules[bundle] = fn;
|
||||
}
|
||||
};
|
||||
|
||||
require.list = function() {
|
||||
var result = [];
|
||||
for (var item in modules) {
|
||||
if (has.call(modules, item)) {
|
||||
result.push(item);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
require.brunch = true;
|
||||
require._cache = cache;
|
||||
globals.require = require;
|
||||
})();
|
||||
|
11
libresapi/src/webui-src/package.json
Normal file
11
libresapi/src/webui-src/package.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "webui_neu",
|
||||
"scripts": {
|
||||
"watch": "brunch watch --server"
|
||||
},
|
||||
"devDependencies": {
|
||||
"auto-reload-brunch": "^1.8.0",
|
||||
"brunch": "^1.8.5",
|
||||
"sass-brunch": "^1.9.1"
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
REACT_VERSION = 0.13.1
|
||||
|
||||
DISTDIR = ../webfiles
|
||||
JSEXTLIBS = $(DISTDIR)/react.js $(DISTDIR)/JSXTransformer.js
|
||||
JSLIBS = RsXHRConnection.js RsApi.js
|
||||
HTML = index.html
|
||||
JSGUI = gui.jsx
|
||||
CSS = green-black.css
|
||||
|
||||
all: $(DISTDIR) $(JSEXTLIBS) $(addprefix $(DISTDIR)/, $(JSLIBS)) $(addprefix $(DISTDIR)/, $(HTML)) $(addprefix $(DISTDIR)/, $(JSGUI)) $(addprefix $(DISTDIR)/, $(CSS))
|
||||
.PHONY: all
|
||||
|
||||
$(DISTDIR)/livereload: $(DISTDIR) $(JSEXTLIBS) $(addprefix $(DISTDIR)/, $(JSLIBS)) $(addprefix $(DISTDIR)/, $(HTML)) $(addprefix $(DISTDIR)/, $(JSGUI)) $(addprefix $(DISTDIR)/, $(CSS))
|
||||
wget -qO- http://localhost:9090/api/v2/livereload/trigger
|
||||
touch $(DISTDIR)/livereload
|
||||
|
||||
$(DISTDIR)/react.js:
|
||||
cd $(DISTDIR) && wget --no-check-certificate --output-document react.js http://fb.me/react-$(REACT_VERSION).js
|
||||
|
||||
$(DISTDIR)/JSXTransformer.js:
|
||||
cd $(DISTDIR) && wget --no-check-certificate --output-document JSXTransformer.js http://fb.me/JSXTransformer-$(REACT_VERSION).js
|
||||
|
||||
$(addprefix $(DISTDIR)/, $(JSLIBS)): $(DISTDIR)/%: %
|
||||
cp $< $@
|
||||
|
||||
$(addprefix $(DISTDIR)/, $(HTML)): $(DISTDIR)/%: %
|
||||
cp $< $@
|
||||
|
||||
$(addprefix $(DISTDIR)/, $(JSGUI)): $(DISTDIR)/%: %
|
||||
cp $< $@
|
||||
|
||||
$(addprefix $(DISTDIR)/, $(CSS)): $(DISTDIR)/%: %
|
||||
cp $< $@
|
||||
|
||||
$(DISTDIR):
|
||||
mkdir $(DISTDIR)
|
@ -1,142 +0,0 @@
|
||||
var TypesMod = require("./Types.js");
|
||||
var Type = TypesMod.Type;
|
||||
var string = TypesMod.string;
|
||||
var bool = TypesMod.bool;
|
||||
var any = TypesMod.any;
|
||||
|
||||
if(require.main === module)
|
||||
{
|
||||
var RsNodeHttpConnection = require("./RsNodeHttpConnection.js");
|
||||
debugger;
|
||||
var connection = new RsNodeHttpConnection();
|
||||
var RsApi = require("./RsApi.js");
|
||||
var RS = new RsApi(connection);
|
||||
|
||||
var tests = [];
|
||||
var doc = {
|
||||
counter: 0,
|
||||
toc: [],
|
||||
content: [],
|
||||
header: function(h){
|
||||
this.toc.push(h);
|
||||
this.content.push("<a name=\""+this.counter+"\"><h1>"+h+"</h1></a>");
|
||||
this.counter += 1;
|
||||
},
|
||||
paragraph: function(p){
|
||||
this.content.push("<p>"+p+"</p>");
|
||||
},
|
||||
|
||||
};
|
||||
PeersTest(tests, doc);
|
||||
|
||||
var docstr = "<!DOCTYPE html><html><body>";
|
||||
docstr += "<h1>Table of Contents</h1>";
|
||||
docstr += "<ul>";
|
||||
for(var i in doc.toc)
|
||||
{
|
||||
docstr += "<li><a href=\"#"+i+"\">"+doc.toc[i]+"</a></li>";
|
||||
}
|
||||
docstr += "</ul>";
|
||||
for(var i in doc.content)
|
||||
{
|
||||
docstr += doc.content[i];
|
||||
}
|
||||
docstr += "</body></html>";
|
||||
|
||||
var fs = require('fs');
|
||||
fs.writeFile("dist/api_documentation.html", docstr);
|
||||
|
||||
tests.map(function(test){
|
||||
test(RS);
|
||||
});
|
||||
}
|
||||
|
||||
function PeersTest(tests, doc)
|
||||
{
|
||||
// compound types
|
||||
var location = new Type("location",
|
||||
{
|
||||
avatar_address: string,
|
||||
groups: any,
|
||||
is_online: bool,
|
||||
location: string,
|
||||
peer_id: any,
|
||||
});
|
||||
var peer_info = new Type("peer_info",
|
||||
{
|
||||
name: string,
|
||||
pgp_id: any,
|
||||
locations: [location],
|
||||
});
|
||||
var peers_list = new Type("peers_list",[peer_info]);
|
||||
|
||||
doc.header("peers");
|
||||
doc.paragraph("<pre>"+graphToText(peers_list)+"</pre>");
|
||||
|
||||
tests.push(function(RS){
|
||||
console.log("testing peers module...");
|
||||
console.log("expected schema is:")
|
||||
console.log(graphToText(peers_list));
|
||||
RS.request({path: "peers"}, function(resp){
|
||||
//console.log("got response:"+JSON.stringify(resp));
|
||||
var ok = peers_list.check(function(str){console.log(str);}, resp.data, [])
|
||||
if(ok)
|
||||
console.log("success");
|
||||
else
|
||||
console.log("fail");
|
||||
});
|
||||
});
|
||||
|
||||
function graphToText(top_node)
|
||||
{
|
||||
//var dbg = function(str){console.log(str);};
|
||||
var dbg = function(str){};
|
||||
|
||||
dbg("called graphToText with " + top_node);
|
||||
|
||||
var res = "";
|
||||
|
||||
_visit(top_node.getObj(), 0);
|
||||
|
||||
return res;
|
||||
|
||||
function _indent(count)
|
||||
{
|
||||
var str = "";
|
||||
for(var i = 0; i < count; i++)
|
||||
{
|
||||
str = str + " ";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
function _visit(node, indent)
|
||||
{
|
||||
dbg("_visit");
|
||||
if(node instanceof Array)
|
||||
{
|
||||
dbg("is instanceof Array");
|
||||
//res = res + "[";
|
||||
res = res + "array\n";
|
||||
_visit(node[0], indent);
|
||||
//res = res + _indent(indent) + "]\n";
|
||||
}
|
||||
else if(node instanceof Type && node.isLeaf())
|
||||
{
|
||||
dbg("is instanceof Type");
|
||||
res = res + node.getName() + "\n";
|
||||
}
|
||||
else // Object, have to check all children
|
||||
{
|
||||
dbg("is Object");
|
||||
//res = res + "{\n";
|
||||
for(m in node.getObj())
|
||||
{
|
||||
res = res + _indent(indent+1) + m + ": ";
|
||||
_visit(node.getObj()[m], indent+1);
|
||||
}
|
||||
//res = res + _indent(indent) + "}\n";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
A new approach to build a webinterface for RS
|
||||
=============================================
|
||||
|
||||
1. get JSON encoded data from the backend, data contains a state token
|
||||
2. render data with react.js
|
||||
3. ask the backend if the state token from step 1 expired. If yes, then start again with step 1.
|
||||
|
||||
Steps 1. and 3. are common for most things, only Step 2. differs. This allows to re-use code for steps 1. and 3.
|
||||
|
||||
BUILD / INSTALLATION
|
||||
------------
|
||||
|
||||
- run (requires wget, use MinGW shell on Windows)
|
||||
make
|
||||
- all output files are now in libresapi/src/webfiles
|
||||
- use the --webinterface 9090 command line parameter to enable webui in retroshare-nogui
|
||||
- set the --docroot parameter of retroshare-nogui to point to the "libresapi/src/webfiles" directory
|
||||
(or symlink from /usr/share/RetroShare06/webui on Linux, ./webui on Windows)
|
||||
- retroshare-gui does not have a --docroot parameter. Use symlinks then.
|
||||
|
||||
DEVELOPMENT
|
||||
-----------
|
||||
|
||||
- Ubuntu: install nodejs package
|
||||
sudo apt-get install nodejs
|
||||
- Windows: download and install nodejs from http://nodejs.org
|
||||
- Download development tools with the nodejs package manager (short npm)
|
||||
npm install
|
||||
- run Retroshare with webinterface on port 9090
|
||||
- during development, run this command (use MinGW shell on Windows)
|
||||
while true; do make ../webfiles/livereload --silent; sleep 1; done
|
||||
- the command will copy the source files to libresapi/src/webfiles if they change
|
||||
- it will trigger livereload at http://localhost:9090/api/v2/livereload/trigger
|
||||
|
||||
API DOCUMENTATION
|
||||
-----------------
|
||||
|
||||
- run
|
||||
node PeersTest.js
|
||||
- this will print the expected schema of the api output, and it will try to test it with real data
|
||||
- run retroshare-nogui with webinterface enabled on port 9090, to test if the real output of the api matches the expected schema
|
||||
|
||||
CONTRIBUTE
|
||||
----------
|
||||
|
||||
- if you are a web developer or want to become one
|
||||
get in contact!
|
||||
- lots of work to do, i need you!
|
@ -1,109 +0,0 @@
|
||||
/**
|
||||
* JS Api for Retroshare
|
||||
* @constructor
|
||||
* @param {object} connection - an object which implements a request() function.
|
||||
* The request function should take two parameters: an object to be send as request and a callback.
|
||||
* The callback should get called with an response object on success.
|
||||
*/
|
||||
function RsApi(connection)
|
||||
{
|
||||
var runnign = true;
|
||||
/**
|
||||
* Send a request to the server
|
||||
* @param req - the request so send
|
||||
* @param {Function} cb - callback function which takes the response as parameter
|
||||
*/
|
||||
this.request = function(req, cb)
|
||||
{
|
||||
connection.request(req, cb);
|
||||
};
|
||||
var tokenlisteners = [];
|
||||
/**
|
||||
* Register a callback to be called when the state token expired.
|
||||
* @param {Function} listener - the callback function, which does not take arguments
|
||||
* @param token - the state token to listen for
|
||||
*/
|
||||
this.register_token_listener = function(listener, token)
|
||||
{
|
||||
tokenlisteners.push({listener:listener, token:token});
|
||||
};
|
||||
/**
|
||||
* Unregister a previously registered callback.
|
||||
*/
|
||||
this.unregister_token_listener = function(listener) // no token as parameter, assuming unregister from all listening tokens
|
||||
{
|
||||
var to_delete = [];
|
||||
for(var i=0; i<tokenlisteners.length; i++){
|
||||
if(tokenlisteners[i].listener === listener){
|
||||
to_delete.push(i);
|
||||
}
|
||||
}
|
||||
for(var i=0; i<to_delete.length; i++){
|
||||
// copy the last element to the current index
|
||||
var index = to_delete[i];
|
||||
tokenlisteners[index] = tokenlisteners[tokenlisteners.length-1];
|
||||
// remove last element
|
||||
tokenlisteners.pop();
|
||||
}
|
||||
};
|
||||
/**
|
||||
* start polling for state changes
|
||||
*/
|
||||
this.start = function(){
|
||||
running = true;
|
||||
setTimeout(tick, TICK_INTERVAL);
|
||||
}
|
||||
/**
|
||||
* stop polling for state changes
|
||||
*/
|
||||
this.stop = function(){
|
||||
running = false;
|
||||
}
|
||||
|
||||
// ************** interal stuff **************
|
||||
var TICK_INTERVAL = 3000;
|
||||
function received_tokenstates(resp)
|
||||
{
|
||||
if(resp.data){
|
||||
for(var i=0; i<resp.data.length; i++){
|
||||
var token = resp.data[i];
|
||||
// search the listener for this token
|
||||
for(var j=0; j<tokenlisteners.length; j++){
|
||||
if(tokenlisteners[j].token === token){
|
||||
// call the listener
|
||||
tokenlisteners[j].listener();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// schedule new update
|
||||
if(running)
|
||||
setTimeout(tick, TICK_INTERVAL);
|
||||
};
|
||||
function received_error()
|
||||
{
|
||||
// try again, maybe want a better logic later
|
||||
if(running)
|
||||
setTimeout(tick, TICK_INTERVAL);
|
||||
};
|
||||
function tick()
|
||||
{
|
||||
var data = [];
|
||||
// maybe cache the token list?
|
||||
// profiler will tell us if we should
|
||||
for(var i=0; i<tokenlisteners.length; i++){
|
||||
data.push(tokenlisteners[i].token);
|
||||
}
|
||||
connection.request({
|
||||
path: "statetokenservice",
|
||||
data: data,
|
||||
}, received_tokenstates, received_error);
|
||||
};
|
||||
};
|
||||
|
||||
// with this trick, we should be able to run in browser or nodejs
|
||||
if(typeof window === 'undefined')
|
||||
{
|
||||
// we are running in nodejs, so have to add to export
|
||||
module.exports = RsApi;
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
var http = require('http');
|
||||
|
||||
/**
|
||||
* Connection to the RS backend using http for running under node.js
|
||||
* Mainly for testing, but could also use it for general purpose scripting.
|
||||
* @constructor
|
||||
*/
|
||||
module.exports = function()
|
||||
{
|
||||
var server_hostname = "localhost";
|
||||
var server_port = "9090";
|
||||
var api_root_path = "/api/v2/";
|
||||
|
||||
this.request = function(request, callback)
|
||||
{
|
||||
var data;
|
||||
if(request.data)
|
||||
data = JSON.stringify(request.data);
|
||||
else
|
||||
data = "";
|
||||
|
||||
// NODEJS specific
|
||||
var req = http.request({
|
||||
host: server_hostname,
|
||||
port: server_port,
|
||||
path: api_root_path + request.path,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Content-Length": data.length, // content length is required, else Wt will not provide the data (maybe WT does not like chunked encoding?)
|
||||
}
|
||||
//method: "POST",
|
||||
}, function(response){
|
||||
var databuffer = [];
|
||||
response.on("data", function(chunk){
|
||||
//console.log("got some data");
|
||||
databuffer = databuffer + chunk;
|
||||
})
|
||||
response.on("end", function(){
|
||||
//console.log("finished receiving data");
|
||||
//console.log("data:"+databuffer);
|
||||
callback(JSON.parse(databuffer));
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
//console.log("uploading data:");
|
||||
//console.log(data);
|
||||
req.write(data);
|
||||
req.end();
|
||||
}
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
/**
|
||||
* Connection to the RS backend using XHR
|
||||
* (could add other connections later, for example WebSockets)
|
||||
* @constructor
|
||||
*/
|
||||
function RsXHRConnection(server_hostname, server_port)
|
||||
{
|
||||
var debug;
|
||||
//debug = function(str){console.log(str);};
|
||||
debug = function(str){};
|
||||
|
||||
//server_hostname = "localhost";
|
||||
//server_port = "9090";
|
||||
var api_root_path = "/api/v2/";
|
||||
|
||||
var status_listeners = [];
|
||||
|
||||
function notify_status(status)
|
||||
{
|
||||
for(var i = 0; i < status_listeners.length; i++)
|
||||
{
|
||||
status_listeners[i](status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be called when the state of the connection changes.
|
||||
* @param {function} cb - callback which receives a single argument. The arguments value is "connected" or "not_connected".
|
||||
*/
|
||||
this.register_status_listener = function(cb)
|
||||
{
|
||||
status_listeners.push(cb);
|
||||
};
|
||||
|
||||
/**
|
||||
* Unregister a status callback function.
|
||||
* @param {function} cb - a privously registered callback function
|
||||
*/
|
||||
this.unregister_status_listener = function(cb)
|
||||
{
|
||||
var to_delete = [];
|
||||
for(var i = 0; i < status_listeners.length; i++)
|
||||
{
|
||||
if(status_listeners[i] === cb){
|
||||
to_delete.push(i);
|
||||
}
|
||||
}
|
||||
for(var i = 0; i < to_delete.length; i++)
|
||||
{
|
||||
// copy the last element to the current index
|
||||
var index = to_delete[i];
|
||||
status_listeners[i] = status_listeners[status_listeners.length-1];
|
||||
// remove the last element
|
||||
status_listeners.pop();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a request to the backend
|
||||
* automatically encodes the request as JSON before sending it to the server
|
||||
* @param {object} req - the request to send to the server
|
||||
* @param {function} cb - callback function to be called to handle the response. The callback takes one object as parameter. Can be left undefined.
|
||||
* @param {function} err_cb - callback function to signal a failed request. Can be undefined.
|
||||
*/
|
||||
this.request = function(req, cb, err_cb)
|
||||
{
|
||||
//var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
|
||||
// TODO: window is not available in QML
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = function(){
|
||||
//console.log("onreadystatechanged state"+xhr.readyState);
|
||||
// TODO: figure out how to catch errors like connection refused
|
||||
// maybe want to have to set a state variable like ok=false
|
||||
// the gui could then display: "no connection to server"
|
||||
if (xhr.readyState === 4) {
|
||||
if(xhr.status !== 200)
|
||||
{
|
||||
console.log("RsXHRConnection: request failed with status: "+xhr.status);
|
||||
console.log("request was:");
|
||||
console.log(req);
|
||||
notify_status("not_connected");
|
||||
if(err_cb !== undefined)
|
||||
err_cb();
|
||||
return;
|
||||
}
|
||||
// received response
|
||||
notify_status("connected");
|
||||
debug("RsXHRConnection received response:");
|
||||
debug(xhr.responseText);
|
||||
if(false)//if(xhr.responseText === "")
|
||||
{
|
||||
debug("Warning: response is empty");
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
var respObj = JSON.parse(xhr.responseText);
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
debug("Exception during response handling: "+e);
|
||||
}
|
||||
if(cb === undefined)
|
||||
debug("No callback function specified");
|
||||
else
|
||||
cb(respObj);
|
||||
}
|
||||
}
|
||||
// post is required for sending data
|
||||
var method;
|
||||
if(req.data){
|
||||
method = "POST";
|
||||
} else {
|
||||
method = "GET";
|
||||
}
|
||||
xhr.open(method, "http://"+server_hostname+":"+server_port+api_root_path+req.path);
|
||||
var data = JSON.stringify(req.data);
|
||||
debug("RsXHRConnection sending data:");
|
||||
debug(data);
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
xhr.send(data);
|
||||
};
|
||||
};
|
@ -1,146 +0,0 @@
|
||||
/**
|
||||
* Construct a new type from an array, object or function.
|
||||
* Use instances of this class to build a schema graph.
|
||||
* The schema graph must not contain data other than arrays and instances of this class.
|
||||
* Use the check function to check if arbitrary JS objects matches the schema.
|
||||
* @constructor
|
||||
* @param {String} name - name for the new type
|
||||
* @param obj - array, object or function
|
||||
* array: array should contain one instance of class "Type"
|
||||
* object: object members can be arrays or instances of class "Type".
|
||||
* Can also have child objects, but the leaf members have to be instances of class "Type"
|
||||
* function: a function which takes three parameters: log, other, stack
|
||||
* must return true if other matches the type, can report errors using the function passed in log.
|
||||
*/
|
||||
function Type(name, obj)
|
||||
{
|
||||
//var dbg = function(str){console.log(str);};
|
||||
var dbg = function(str){};
|
||||
|
||||
this.getName = function(){
|
||||
return name;
|
||||
}
|
||||
this.isLeaf = function(){
|
||||
return typeof(obj) === "function";
|
||||
}
|
||||
this.getObj = function(){
|
||||
return obj;
|
||||
}
|
||||
this.check = function(log, other, stack)
|
||||
{
|
||||
if(typeof(obj) === "object")
|
||||
{
|
||||
stack.push("<"+name+">");
|
||||
var ok = _check(log, obj, other, stack);
|
||||
stack.pop;
|
||||
return ok;
|
||||
}
|
||||
if(typeof(obj) === "function")
|
||||
return obj(log, other, stack);
|
||||
log("FATAL Error: wrong usage of new Type(), second parameter should be an object or checker function");
|
||||
return false;
|
||||
}
|
||||
function _check(log, ref, other, stack)
|
||||
{
|
||||
dbg("_check");
|
||||
dbg("ref=" + ref);
|
||||
dbg("other=" + other);
|
||||
dbg("stack=[" + stack + "]");
|
||||
if(ref instanceof Array)
|
||||
{
|
||||
dbg("is instanceof Array");
|
||||
if(other instanceof Array)
|
||||
{
|
||||
if(other.length > 0)
|
||||
{
|
||||
return _check(log, ref[0], other[0], stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
log("Warning: can't check array of length 0 in ["+stack+"]");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log("Error: not an Array ["+stack+"]");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(ref instanceof Type)
|
||||
{
|
||||
dbg("is instanceof Type");
|
||||
return ref.check(log, other, stack);
|
||||
}
|
||||
else // Object, have to check all children
|
||||
{
|
||||
dbg("is Object");
|
||||
var ok = true;
|
||||
for(m in ref)
|
||||
{
|
||||
if(m in other)
|
||||
{
|
||||
stack.push(m);
|
||||
ok = ok && _check(log, ref[m], other[m], stack);
|
||||
stack.pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
log("Error: missing member \""+m+"\" in ["+stack+"]");
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
// check for additionally undocumented members
|
||||
for(m in other)
|
||||
{
|
||||
if(!(m in ref))
|
||||
{
|
||||
log("Warning: found additional member \""+m+"\" in ["+stack+"]");
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
// basic data types
|
||||
// - string
|
||||
// - bool
|
||||
// - any (placeholder for unknown type)
|
||||
|
||||
var string = new Type("string",
|
||||
function(log, other, stack)
|
||||
{
|
||||
if(typeof(other) !== "string")
|
||||
{
|
||||
log("Error: not a string ["+stack+"]");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
);
|
||||
var bool = new Type("bool",
|
||||
function(log, other, stack)
|
||||
{
|
||||
if(typeof(other) !== "boolean")
|
||||
{
|
||||
log("Error: not a bool ["+stack+"]");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
);
|
||||
var any = new Type("any",
|
||||
function(log, other, stack)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
exports.Type = Type;
|
||||
exports.string = string;
|
||||
exports.bool = bool;
|
||||
exports.any = any;
|
@ -1,134 +0,0 @@
|
||||
body {
|
||||
background-color: black;
|
||||
color: lime;
|
||||
font-family: monospace;
|
||||
margin: 0em;
|
||||
/*padding: 1.5em;*/
|
||||
padding: 2mm;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
#overlay{
|
||||
z-index: 10;
|
||||
position: fixed;
|
||||
top:0;
|
||||
left:0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0,0,0,0.8);
|
||||
}
|
||||
|
||||
.paddingbox{
|
||||
padding:2mm;
|
||||
}
|
||||
|
||||
.nav{
|
||||
list-style-type: none;
|
||||
padding: 0em;
|
||||
margin: 0em;
|
||||
}
|
||||
|
||||
.nav li{
|
||||
display: inline;
|
||||
padding: 0.1em;
|
||||
margin-right: 1em;
|
||||
border-width: 0.1em;
|
||||
border-color: blue;
|
||||
border-bottom-style: solid;
|
||||
cursor: pointer;
|
||||
}
|
||||
td{
|
||||
padding: 0.3em;
|
||||
border-style: solid;
|
||||
border-width: 0.1em;
|
||||
border-color: lime;
|
||||
}
|
||||
.btn{
|
||||
border-style: solid;
|
||||
border-color: lime;
|
||||
border-width: 0.1em;
|
||||
cursor: pointer;
|
||||
padding: 0.1em;
|
||||
}
|
||||
|
||||
.btn2, .box{
|
||||
border-style: solid;
|
||||
/*border-color: lime;*/
|
||||
border-color: limeGreen;
|
||||
/*border-width: 1px;*/
|
||||
border-radius: 3mm;
|
||||
padding: 2mm;
|
||||
font-size: 10mm;
|
||||
cursor: pointer;
|
||||
margin-bottom: 2mm;
|
||||
}
|
||||
.btn2:hover{
|
||||
background-color: midnightblue;
|
||||
}
|
||||
|
||||
.filelink{
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
input, textarea{
|
||||
color: lime;
|
||||
font-family: monospace;
|
||||
background-color: black;
|
||||
border-color: lime;
|
||||
font-size: 10mm;
|
||||
border-radius: 3mm;
|
||||
border-width: 1mm;
|
||||
padding: 2mm;
|
||||
margin-bottom: 2mm;
|
||||
margin-right: 2mm;
|
||||
|
||||
/* make the button the whole screen width */
|
||||
width: 100%;
|
||||
/* make the text input fit small screens*/
|
||||
box-sizing: border-box;
|
||||
}
|
||||
input:hover{
|
||||
background-color: midnightblue;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.flexbox{
|
||||
display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */
|
||||
display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */
|
||||
display: -ms-flexbox; /* TWEENER - IE 10 */
|
||||
display: -webkit-flex; /* NEW - Chrome */
|
||||
display: flex; /* NEW, Spec - Opera 12.1, Firefox 20+ */
|
||||
}
|
||||
|
||||
.flexwidemember{
|
||||
-webkit-box-flex: 1; /* OLD - iOS 6-, Safari 3.1-6 */
|
||||
-moz-box-flex: 1; /* OLD - Firefox 19- */
|
||||
width: 20%; /* For old syntax, otherwise collapses. */
|
||||
-webkit-flex: 1; /* Chrome */
|
||||
-ms-flex: 1; /* IE 10 */
|
||||
flex: 1; /* NEW, Spec - Opera 12.1, Firefox 20+ */
|
||||
}
|
||||
|
||||
#logo_splash{
|
||||
-webkit-animation-fill-mode: forwards; /* Chrome, Safari, Opera */
|
||||
animation-fill-mode: forwards;
|
||||
-webkit-animation-name: logo_splash; /* Chrome, Safari, Opera */
|
||||
-webkit-animation-duration: 3s; /* Chrome, Safari, Opera */
|
||||
animation-name: logo_splash;
|
||||
animation-duration: 3s;
|
||||
text-align: center;
|
||||
}
|
||||
/* Chrome, Safari, Opera */
|
||||
@-webkit-keyframes logo_splash {
|
||||
from {opacity: 0;}
|
||||
to {opacity: 1;}
|
||||
}
|
||||
|
||||
/* Standard syntax */
|
||||
@keyframes logo_splash {
|
||||
from {opacity: 0;}
|
||||
to {opacity: 1;}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,29 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>New webinterface for Retroshare</title>
|
||||
|
||||
<script src="RsXHRConnection.js"></script>
|
||||
<script src="RsApi.js"></script>
|
||||
|
||||
<!-- it seems to work more reliable, if the jsx file is loaded before react -->
|
||||
<script type="text/jsx" src="gui.jsx"></script>
|
||||
|
||||
<script src="react.js"></script>
|
||||
<script src="JSXTransformer.js"></script>
|
||||
|
||||
<link href="green-black.css" rel="stylesheet">
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
document.write("<p>loading lots of stuff...</p>");
|
||||
</script>
|
||||
<p><noscript>The Retroshare web interface requires JavaScript. Please enable JavaScript in your browser.</noscript></p>
|
||||
<!--<div id="logo_splash">
|
||||
<img src="img/logo_splash.png"></img>
|
||||
</div>-->
|
||||
</body>
|
||||
</html>
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"name": "rswebui",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
},
|
||||
"devDependencies": {
|
||||
},
|
||||
"scripts": {
|
||||
}
|
||||
}
|
@ -307,7 +307,6 @@ public:
|
||||
bool isBroadcast() const;
|
||||
|
||||
RsPeerId toPeerId() const;
|
||||
RsGxsId toGxsId() const;
|
||||
ChatLobbyId toLobbyId() const;
|
||||
DistantChatPeerId toDistantChatId() const;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user