WIP Merging basic player to master #6
@ -23,6 +23,7 @@ set(SOURCES
|
||||
)
|
||||
|
||||
add_executable(corax ${HEADERS} ${SOURCES})
|
||||
add_subdirectory(models)
|
||||
|
||||
target_link_libraries(corax Qt5::Core)
|
||||
target_link_libraries(corax Qt5::Network)
|
||||
@ -31,10 +32,10 @@ target_link_libraries(corax wSocket)
|
||||
target_link_libraries(corax wDispatcher)
|
||||
target_link_libraries(corax utils)
|
||||
target_link_libraries(corax wModel)
|
||||
target_link_libraries(corax wController)
|
||||
target_link_libraries(corax wServerUtils)
|
||||
target_link_libraries(corax wDatabase)
|
||||
target_link_libraries(corax tag)
|
||||
target_link_libraries(corax tools)
|
||||
target_link_libraries(corax coraxModels)
|
||||
target_link_libraries(corax wServerUtils)
|
||||
|
||||
install(TARGETS corax RUNTIME DESTINATION bin)
|
||||
|
@ -9,6 +9,7 @@ Corax* Corax::corax = 0;
|
||||
|
||||
Corax::Corax(QObject *parent):
|
||||
QObject(parent),
|
||||
started(false),
|
||||
server(new W::Server(W::String(u"Corax"), this)),
|
||||
logger(new W::Logger()),
|
||||
parentReporter(new W::ParentReporter()),
|
||||
@ -17,7 +18,8 @@ Corax::Corax(QObject *parent):
|
||||
connector(0),
|
||||
dispatcher(new W::Dispatcher()),
|
||||
caches(),
|
||||
parsers()
|
||||
parsers(),
|
||||
players()
|
||||
{
|
||||
if (corax != 0)
|
||||
{
|
||||
@ -41,7 +43,8 @@ Corax::Corax(QObject *parent):
|
||||
|
||||
attributes->addAttribute(W::String(u"connectionsCount"), new M::String(W::String(u"0"), W::Address({u"attributes", u"connectionCount"})));
|
||||
attributes->addAttribute(W::String(u"name"), new M::String(W::String(u"Corax"), W::Address({u"attributes", u"name"})));
|
||||
attributes->addAttribute(W::String(u"version"), new M::String(W::String(u"0.0.2"), W::Address({u"attributes", u"version"})));
|
||||
attributes->addAttribute(W::String(u"version"), new M::String(W::String(u"0.0.3"), W::Address({u"attributes", u"version"})));
|
||||
attributes->addAttribute(W::String(u"players"), new M::String(W::String(u"0"), W::Address({u"attributes", u"players"})));
|
||||
|
||||
createCaches();
|
||||
createHandlers();
|
||||
@ -49,20 +52,27 @@ Corax::Corax(QObject *parent):
|
||||
|
||||
Corax::~Corax()
|
||||
{
|
||||
std::map<W::String, Parser*>::iterator pbeg = parsers.begin();
|
||||
std::map<W::String, Parser*>::iterator pend = parsers.end();
|
||||
std::map<W::String, Parser*>::const_iterator pbeg = parsers.begin();
|
||||
std::map<W::String, Parser*>::const_iterator pend = parsers.end();
|
||||
|
||||
for (; pbeg != pend; ++pbeg) {
|
||||
delete pbeg->second;
|
||||
}
|
||||
|
||||
std::map<W::String, ResourceCache*>::iterator beg = caches.begin();
|
||||
std::map<W::String, ResourceCache*>::iterator end = caches.end();
|
||||
std::map<W::String, ResourceCache*>::const_iterator beg = caches.begin();
|
||||
std::map<W::String, ResourceCache*>::const_iterator end = caches.end();
|
||||
|
||||
for (; beg != end; ++beg) {
|
||||
delete beg->second;
|
||||
}
|
||||
|
||||
std::map<W::Address, M::Player*>::const_iterator plit = players.begin();
|
||||
std::map<W::Address, M::Player*>::const_iterator plend = players.end();
|
||||
|
||||
for (; plit != plend; ++plit) {
|
||||
delete plit->second;
|
||||
}
|
||||
|
||||
delete connector;
|
||||
|
||||
dispatcher->unregisterDefaultHandler(logger);
|
||||
@ -83,6 +93,9 @@ void Corax::onConnectionCountChanged(uint64_t count)
|
||||
|
||||
void Corax::start()
|
||||
{
|
||||
if (started) {
|
||||
throw 3;
|
||||
}
|
||||
std::map<W::String, ResourceCache*>::iterator beg = caches.begin();
|
||||
std::map<W::String, ResourceCache*>::iterator end = caches.end();
|
||||
|
||||
@ -90,11 +103,18 @@ void Corax::start()
|
||||
server->listen(8080);
|
||||
|
||||
cout << "Registering models..." << endl;
|
||||
attributes->registerModel(dispatcher, server);
|
||||
commands->registerModel(dispatcher, server);
|
||||
attributes->getRegistered(connector);
|
||||
commands->getRegistered(connector);
|
||||
|
||||
for (; beg != end; ++beg) {
|
||||
beg->second->registerModel(dispatcher, server);
|
||||
beg->second->getRegistered(connector);
|
||||
}
|
||||
|
||||
std::map<W::Address, M::Player*>::const_iterator plit = players.begin();
|
||||
std::map<W::Address, M::Player*>::const_iterator plend = players.end();
|
||||
|
||||
for (; plit != plend; ++plit) {
|
||||
plit->second->getRegistered(connector);;
|
||||
}
|
||||
|
||||
cout << "Opening caches..." << endl;
|
||||
@ -105,24 +125,40 @@ void Corax::start()
|
||||
}
|
||||
|
||||
commands->enableCommand(W::String(u"clearCache"), true);
|
||||
commands->enableCommand(W::String(u"givePlayer"), true);
|
||||
|
||||
started = true;
|
||||
cout << "Corax is ready" << endl;
|
||||
}
|
||||
|
||||
void Corax::stop()
|
||||
{
|
||||
if (!started) {
|
||||
throw 3;
|
||||
}
|
||||
|
||||
std::map<W::String, ResourceCache*>::iterator beg = caches.begin();
|
||||
std::map<W::String, ResourceCache*>::iterator end = caches.end();
|
||||
|
||||
cout << "Stopping corax..." << endl;
|
||||
commands->unregisterModel();
|
||||
attributes->unregisterModel();
|
||||
commands->getUnregistered();
|
||||
attributes->getUnregistered();
|
||||
|
||||
for (; beg != end; ++beg) {
|
||||
beg->second->unregisterModel();
|
||||
beg->second->getUnregistered();
|
||||
}
|
||||
|
||||
std::map<W::Address, M::Player*>::const_iterator plit = players.begin();
|
||||
std::map<W::Address, M::Player*>::const_iterator plend = players.end();
|
||||
|
||||
for (; plit != plend; ++plit) {
|
||||
plit->second->getUnregistered();
|
||||
}
|
||||
|
||||
server->stop();
|
||||
|
||||
started = false;
|
||||
cout << "Corax is stopped" << endl;
|
||||
}
|
||||
|
||||
void Corax::onModelServiceMessage(const QString& msg)
|
||||
@ -169,10 +205,10 @@ void Corax::h_parseDirectory(const W::Event& ev)
|
||||
if (itr != parsers.end()) {
|
||||
cout << "directory " << path.toString() << " is already being parsed" << endl;
|
||||
} else {
|
||||
const W::Socket& socket = connector->getNodeSocket(W::String(u"Perturabo"));
|
||||
const W::Socket* socket = connector->getNodeSocket(W::String(u"Perturabo"));
|
||||
ResourceCache* music = caches.at(W::String(u"music"));
|
||||
ResourceCache* images = caches.at(W::String(u"images"));
|
||||
Parser* parser = new Parser(&socket, dispatcher, music, images);
|
||||
Parser* parser = new Parser(socket, dispatcher, music, images);
|
||||
parsers.insert(std::make_pair(path, parser));
|
||||
|
||||
connect(parser, SIGNAL(serviceMessage(const QString&)), SLOT(onModelServiceMessage(const QString&)));
|
||||
@ -202,6 +238,9 @@ void Corax::createHandlers()
|
||||
W::Vocabulary parseArgs;
|
||||
parseArgs.insert(u"path", W::Uint64(W::Object::string));
|
||||
commands->addCommand(W::String(u"parseDirectory"), parseDirectory, parseArgs);
|
||||
|
||||
W::Handler* givePlayer = W::Handler::create(W::Address({u"management", u"givePlayer"}), this, &Corax::_h_givePlayer);
|
||||
commands->addCommand(W::String(u"givePlayer"), givePlayer, W::Vocabulary());
|
||||
}
|
||||
|
||||
void Corax::onParserDone(const W::String& path)
|
||||
@ -224,6 +263,7 @@ void Corax::onNodeConnected(const W::String& name)
|
||||
cout << "connected node " << name.toString() << endl;
|
||||
if (name == u"Perturabo") {
|
||||
commands->enableCommand(W::String(u"parseDirectory"), true);
|
||||
commands->enableCommand(W::String(u"givePlayer"), true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,5 +272,31 @@ void Corax::onNodeDisconnected(const W::String& name)
|
||||
cout << "disconnected node " << name.toString() << endl;
|
||||
if (name == u"Perturabo") {
|
||||
commands->enableCommand(W::String(u"parseDirectory"), false);
|
||||
commands->enableCommand(W::String(u"givePlayer"), false);
|
||||
}
|
||||
}
|
||||
|
||||
void Corax::h_givePlayer(const W::Event& ev)
|
||||
{
|
||||
W::String num(std::to_string(players.size()));
|
||||
W::Address addr{u"players", num};
|
||||
M::Player* pl = new M::Player(addr);
|
||||
connect(pl, SIGNAL(serviceMessage(const QString&)), SLOT(onModelServiceMessage(const QString&)));
|
||||
players.insert(std::make_pair(addr, pl));
|
||||
|
||||
attributes->setAttribute(W::String(u"players"), W::String(std::to_string(players.size())));
|
||||
pl->getRegistered(connector); //it's a handler, so I assume corax is started here;
|
||||
|
||||
W::Vocabulary* vc = new W::Vocabulary();
|
||||
vc->insert(u"address", addr);
|
||||
|
||||
const W::Vocabulary& svc = static_cast<const W::Vocabulary&>(ev.getData());
|
||||
const W::Address& source = static_cast<const W::Address&>(svc.at(u"source"));
|
||||
uint64_t id = ev.getSenderId();
|
||||
vc->insert(u"source", W::Address{u"management"}); //TODO think about it, may be Corax should be a model?
|
||||
|
||||
W::Event res(source + W::Address{u"getPlayer"}, vc);
|
||||
res.setSenderId(id);
|
||||
connector->getConnection(id)->send(res);
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <wDatabase/resourcecache.h>
|
||||
|
||||
#include "tools/parser.h"
|
||||
#include "models/player.h"
|
||||
|
||||
class Corax: public QObject
|
||||
{
|
||||
@ -42,6 +43,7 @@ public:
|
||||
static Corax* corax;
|
||||
|
||||
private:
|
||||
bool started;
|
||||
W::Server *server;
|
||||
W::Logger *logger;
|
||||
W::ParentReporter* parentReporter;
|
||||
@ -53,9 +55,11 @@ private:
|
||||
|
||||
std::map<W::String, ResourceCache*> caches;
|
||||
std::map<W::String, Parser*> parsers;
|
||||
std::map<W::Address, M::Player*> players;
|
||||
|
||||
handler(clearCache);
|
||||
handler(parseDirectory);
|
||||
handler(givePlayer);
|
||||
|
||||
public slots:
|
||||
void start();
|
||||
|
21
corax/models/CMakeLists.txt
Normal file
21
corax/models/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
project(coraxModels)
|
||||
|
||||
find_package(Qt5Core REQUIRED)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
|
||||
set(HEADERS
|
||||
player.h
|
||||
proxysong.h
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
player.cpp
|
||||
proxysong.cpp
|
||||
)
|
||||
|
||||
add_library(coraxModels STATIC ${HEADERS} ${SOURCES})
|
||||
|
||||
target_link_libraries(coraxModels wModel)
|
313
corax/models/player.cpp
Normal file
313
corax/models/player.cpp
Normal file
@ -0,0 +1,313 @@
|
||||
#include "player.h"
|
||||
|
||||
M::Player::Player(const W::Address& address, QObject* parent):
|
||||
M::Model(address, parent),
|
||||
controls(),
|
||||
views(),
|
||||
playPauseBtn(new M::Button(address + W::Address{u"play"})),
|
||||
nextBtn(new M::Button(address + W::Address{u"next"})),
|
||||
prevBtn(new M::Button(address + W::Address{u"prev"})),
|
||||
_queueView(new M::List(address + W::Address{u"queueView"})),
|
||||
_queue(),
|
||||
current(0),
|
||||
counter(0),
|
||||
currentIndex(0),
|
||||
mode(playBack),
|
||||
playing(false),
|
||||
scheduledToplay(false)
|
||||
{
|
||||
W::Handler* get = W::Handler::create(address + W::Address({u"get"}), this, &M::Player::_h_get);
|
||||
W::Handler* hqueue = W::Handler::create(address + W::Address({u"queue"}), this, &M::Player::_h_queue);
|
||||
W::Handler* hplay = W::Handler::create(address + W::Address({u"play"}), this, &M::Player::_h_play);
|
||||
addHandler(get);
|
||||
addHandler(hqueue);
|
||||
addHandler(hplay);
|
||||
|
||||
playPauseBtn->setLabel(W::String(u"Play"));
|
||||
playPauseBtn->setEnabled(false);
|
||||
connect(playPauseBtn, SIGNAL(activated()), this, SLOT(onPlayPauseBtn()));
|
||||
|
||||
nextBtn->setLabel(W::String(u"Next"));
|
||||
nextBtn->setEnabled(false);
|
||||
connect(nextBtn, SIGNAL(activated()), this, SLOT(onNextBtn()));
|
||||
|
||||
prevBtn->setLabel(W::String(u"Prev"));
|
||||
prevBtn->setEnabled(false);
|
||||
connect(prevBtn, SIGNAL(activated()), this, SLOT(onPrevBtn()));
|
||||
|
||||
addModel(playPauseBtn);
|
||||
addModel(nextBtn);
|
||||
addModel(prevBtn);
|
||||
addModel(_queueView);
|
||||
|
||||
controls.insert(std::make_pair(playPause, playPauseBtn->getAddress()));
|
||||
controls.insert(std::make_pair(next, nextBtn->getAddress()));
|
||||
controls.insert(std::make_pair(prev, prevBtn->getAddress()));
|
||||
views.insert(std::make_pair(queue, _queueView->getAddress()));
|
||||
}
|
||||
|
||||
M::Player::~Player()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void M::Player::set(const W::Object& value)
|
||||
{
|
||||
throw 14; //what do you expect here? not implemented, and not sure it ever would be
|
||||
}
|
||||
|
||||
void M::Player::set(W::Object* value)
|
||||
{
|
||||
set(*value);
|
||||
}
|
||||
|
||||
M::Model::ModelType M::Player::getType() const
|
||||
{
|
||||
return M::Model::player;
|
||||
}
|
||||
|
||||
void M::Player::h_subscribe(const W::Event& ev)
|
||||
{
|
||||
M::Model::h_subscribe(ev);
|
||||
|
||||
h_get(ev);
|
||||
}
|
||||
|
||||
void M::Player::h_get(const W::Event& ev)
|
||||
{
|
||||
W::Vector* ctrls = new W::Vector();
|
||||
ItemMap::const_iterator citr = controls.begin();
|
||||
ItemMap::const_iterator cend = controls.end();
|
||||
|
||||
for (; citr != cend; ++citr) {
|
||||
W::Vocabulary* cvc = new W::Vocabulary();
|
||||
cvc->insert(u"type", new W::Uint64(citr->first));
|
||||
cvc->insert(u"address", citr->second);
|
||||
|
||||
ctrls->push(cvc);
|
||||
}
|
||||
|
||||
W::Vector* vws = new W::Vector();
|
||||
ItemMap::const_iterator vitr = views.begin();
|
||||
ItemMap::const_iterator vend = views.end();
|
||||
|
||||
for (; vitr != vend; ++vitr) {
|
||||
W::Vocabulary* vvc = new W::Vocabulary();
|
||||
vvc->insert(u"type", new W::Uint64(vitr->first));
|
||||
vvc->insert(u"address", vitr->second);
|
||||
|
||||
vws->push(vvc);
|
||||
}
|
||||
|
||||
W::Vocabulary* res = new W::Vocabulary();
|
||||
|
||||
res->insert(u"controls", ctrls);
|
||||
res->insert(u"views", vws);
|
||||
res->insert(u"mode", new W::Uint64(mode));
|
||||
|
||||
response(res, W::Address({u"get"}), ev);
|
||||
}
|
||||
|
||||
void M::Player::onPlayPauseBtn()
|
||||
{
|
||||
if (playing) {
|
||||
pause();
|
||||
} else {
|
||||
play();
|
||||
}
|
||||
}
|
||||
|
||||
void M::Player::h_queue(const W::Event& ev)
|
||||
{
|
||||
const W::Vocabulary& data = static_cast<const W::Vocabulary&>(ev.getData());
|
||||
|
||||
const W::Uint64& id = static_cast<const W::Uint64&>(data.at(u"id"));
|
||||
ProxySong* song = new ProxySong(id, address + W::Address{W::String(W::Uint64(counter++).toString())});
|
||||
addModel(song);
|
||||
_queue.push_back(song);
|
||||
_queueView->push(song->getAddress());
|
||||
|
||||
if (current == 0) {
|
||||
setActive(song);
|
||||
}
|
||||
|
||||
if (currentIndex + 1 < _queue.size()) {
|
||||
nextBtn->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void M::Player::play()
|
||||
{
|
||||
if (!playing) {
|
||||
playPauseBtn->setLabel(W::String(u"Pause"));
|
||||
playing = true;
|
||||
|
||||
switch (mode) {
|
||||
case playBack:
|
||||
if (current == 0) {
|
||||
scheduledToplay = true;
|
||||
} else {
|
||||
if (current->isReady()) {
|
||||
scheduledToplay = false;
|
||||
broadcast(new W::Vocabulary(), W::Address{u"play"});
|
||||
break;
|
||||
} else {
|
||||
scheduledToplay = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void M::Player::pause()
|
||||
{
|
||||
if (playing) {
|
||||
playPauseBtn->setLabel(W::String(u"Play"));
|
||||
playing = false;
|
||||
|
||||
switch (mode) {
|
||||
case playBack:
|
||||
scheduledToplay = false;
|
||||
broadcast(new W::Vocabulary(), W::Address{u"pause"});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void M::Player::onSongReady()
|
||||
{
|
||||
emit serviceMessage("Song is ready");
|
||||
playPauseBtn->setEnabled(true);
|
||||
if (scheduledToplay) {
|
||||
scheduledToplay = false;
|
||||
if (playing) {
|
||||
scheduledToplay = false;
|
||||
broadcast(new W::Vocabulary(), W::Address{u"play"});
|
||||
} else {
|
||||
play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void M::Player::onSongNotReady()
|
||||
{
|
||||
playPauseBtn->setEnabled(false);
|
||||
emit serviceMessage("Something happend to the current song, not sure yet what to do");
|
||||
}
|
||||
|
||||
void M::Player::onNextBtn()
|
||||
{
|
||||
if (currentIndex + 1 < _queue.size()) {
|
||||
if (playing) {
|
||||
pause();
|
||||
scheduledToplay = true;
|
||||
}
|
||||
setActive(currentIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void M::Player::onPrevBtn()
|
||||
{
|
||||
if (currentIndex > 0) {
|
||||
if (playing) {
|
||||
pause();
|
||||
scheduledToplay = true;
|
||||
}
|
||||
setActive(currentIndex - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void M::Player::setActive(ProxySong* song)
|
||||
{
|
||||
if (current == song) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
int index;
|
||||
for (index = 0; index < _queue.size(); ++index) {
|
||||
if (_queue.at(index) == song) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
setActive(index);
|
||||
} else {
|
||||
emit serviceMessage("An attempt to set active a song which is no in the queue, not supposed to happen");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void M::Player::setActive(uint64_t index)
|
||||
{
|
||||
if (index >= _queue.size()) {
|
||||
emit serviceMessage("An attempt to set active a song which is no in the queue, not supposed to happen");
|
||||
return;
|
||||
}
|
||||
|
||||
ProxySong* song = _queue.at(index);
|
||||
currentIndex = index;
|
||||
if (currentIndex + 1 < _queue.size()) {
|
||||
nextBtn->setEnabled(true);
|
||||
} else {
|
||||
nextBtn->setEnabled(false);
|
||||
}
|
||||
|
||||
if (currentIndex > 0) {
|
||||
prevBtn->setEnabled(true);
|
||||
} else {
|
||||
prevBtn->setEnabled(false);
|
||||
}
|
||||
|
||||
W::Vocabulary* res = new W::Vocabulary();
|
||||
W::Vector* add = new W::Vector();
|
||||
W::Vector* remove = new W::Vector();
|
||||
if (current != 0) {
|
||||
disconnect(current, SIGNAL(ready()), this, SLOT(onSongReady()));
|
||||
disconnect(current, SIGNAL(notReady()), this, SLOT(onSongNotReady()));
|
||||
remove->push(new W::Uint64(currentPlayback));
|
||||
}
|
||||
current = song;
|
||||
connect(song, SIGNAL(ready()), this, SLOT(onSongReady()));
|
||||
connect(song, SIGNAL(notReady()), this, SLOT(onSongNotReady()));
|
||||
views.insert(std::make_pair(currentPlayback, song->getAddress()));
|
||||
W::Vocabulary* avc = new W::Vocabulary();
|
||||
avc->insert(u"type", new W::Uint64(currentPlayback));
|
||||
avc->insert(u"address", song->getAddress());
|
||||
|
||||
add->push(avc);
|
||||
|
||||
res->insert(u"add", add);
|
||||
res->insert(u"remove", remove);
|
||||
|
||||
|
||||
broadcast(res, W::Address{u"viewsChange"});
|
||||
if (song->isReady()) {
|
||||
playPauseBtn->setEnabled(true);
|
||||
if (scheduledToplay) {
|
||||
play();
|
||||
}
|
||||
} else {
|
||||
playPauseBtn->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void M::Player::h_play(const W::Event& ev)
|
||||
{
|
||||
const W::Vocabulary& data = static_cast<const W::Vocabulary&>(ev.getData());
|
||||
|
||||
const W::Uint64& id = static_cast<const W::Uint64&>(data.at(u"id"));
|
||||
ProxySong* song = new ProxySong(id, address + W::Address{W::String(W::Uint64(counter++).toString())});
|
||||
addModel(song);
|
||||
_queue.push_back(song);
|
||||
_queueView->push(song->getAddress());
|
||||
|
||||
scheduledToplay = true;
|
||||
setActive(song);
|
||||
|
||||
if (currentIndex + 1 < _queue.size()) {
|
||||
nextBtn->setEnabled(true);
|
||||
}
|
||||
}
|
85
corax/models/player.h
Normal file
85
corax/models/player.h
Normal file
@ -0,0 +1,85 @@
|
||||
#ifndef PLAYER_H
|
||||
#define PLAYER_H
|
||||
|
||||
#include <map>
|
||||
#include <deque>
|
||||
|
||||
#include <wModel/model.h>
|
||||
#include <wModel/button.h>
|
||||
#include <wModel/modelstring.h>
|
||||
#include <wModel/list.h>
|
||||
|
||||
#include <wType/vocabulary.h>
|
||||
#include <wType/vector.h>
|
||||
#include <wType/address.h>
|
||||
|
||||
#include "proxysong.h"
|
||||
/**
|
||||
* @todo write docs
|
||||
*/
|
||||
namespace M {
|
||||
class Player : public M::Model {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Player(const W::Address& address, QObject* parent = 0);
|
||||
~Player();
|
||||
|
||||
void set(const W::Object & value) override;
|
||||
void set(W::Object * value) override;
|
||||
M::Model::ModelType getType() const override;
|
||||
|
||||
enum ItemType {
|
||||
playPause,
|
||||
currentPlayback,
|
||||
queue,
|
||||
picture,
|
||||
prev,
|
||||
next
|
||||
};
|
||||
|
||||
enum Mode {
|
||||
playBack
|
||||
};
|
||||
|
||||
void play();
|
||||
void pause();
|
||||
|
||||
protected:
|
||||
void h_subscribe(const W::Event & ev) override;
|
||||
|
||||
handler(get);
|
||||
handler(queue);
|
||||
handler(play);
|
||||
|
||||
private:
|
||||
typedef std::map<ItemType, W::Address> ItemMap;
|
||||
typedef std::deque<ProxySong*> Queue;
|
||||
|
||||
ItemMap controls;
|
||||
ItemMap views;
|
||||
M::Button* playPauseBtn;
|
||||
M::Button* nextBtn;
|
||||
M::Button* prevBtn;
|
||||
M::List* _queueView;
|
||||
Queue _queue;
|
||||
ProxySong* current;
|
||||
uint64_t counter;
|
||||
uint64_t currentIndex;
|
||||
Mode mode;
|
||||
bool playing;
|
||||
bool scheduledToplay;
|
||||
|
||||
void setActive(ProxySong* song);
|
||||
void setActive(uint64_t index);
|
||||
|
||||
private slots:
|
||||
void onPlayPauseBtn();
|
||||
void onNextBtn();
|
||||
void onPrevBtn();
|
||||
void onSongReady();
|
||||
void onSongNotReady();
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif // PLAYER_H
|
138
corax/models/proxysong.cpp
Normal file
138
corax/models/proxysong.cpp
Normal file
@ -0,0 +1,138 @@
|
||||
#include "proxysong.h"
|
||||
|
||||
ProxySong::ProxySong(const W::Uint64& p_id, const W::Address& p_address, QObject* parent):
|
||||
M::Vocabulary(p_address, parent),
|
||||
songCtrl(new C::Vocabulary(W::Address{u"songs", W::String(p_id.toString())})),
|
||||
albumCtrl(0),
|
||||
artistCtrl(0),
|
||||
fileId(0),
|
||||
_ready(false)
|
||||
{
|
||||
addController(songCtrl, W::String(u"Perturabo"));
|
||||
connect(songCtrl, SIGNAL(newElement(const W::String&, const W::Object&)), SLOT(onSongNewElement(const W::String&, const W::Object&)));
|
||||
connect(songCtrl, SIGNAL(removeElement(const W::String&)), SLOT(onSongRemoveElement(const W::String&)));
|
||||
|
||||
insert(W::String(u"id"), p_id);
|
||||
insert(W::String(u"artist"), new W::String(u"undefined"));
|
||||
insert(W::String(u"album"), new W::String(u"undefined"));
|
||||
insert(W::String(u"song"), new W::String(u"undefined"));
|
||||
insert(W::String(u"image"), new W::Uint64(0));
|
||||
insert(W::String(u"audio"), new W::Uint64(0));
|
||||
}
|
||||
|
||||
bool ProxySong::isReady() const
|
||||
{
|
||||
return _ready;
|
||||
}
|
||||
|
||||
ProxySong::~ProxySong()
|
||||
{
|
||||
}
|
||||
|
||||
void ProxySong::onSongNewElement(const W::String& key, const W::Object& element)
|
||||
{
|
||||
if (key == u"name") {
|
||||
insert(W::String(u"song"), element);
|
||||
} else if (key == u"audio") {
|
||||
if (_ready) {
|
||||
_ready = false;
|
||||
emit notReady();
|
||||
}
|
||||
|
||||
fileId = static_cast<const W::Uint64&>(element);
|
||||
insert(key, element);
|
||||
_ready = true;
|
||||
emit ready();
|
||||
} else if (key == u"artist") {
|
||||
if (artistCtrl != 0) {
|
||||
removeController(artistCtrl);
|
||||
disconnect(artistCtrl, SIGNAL(newElement(const W::String&, const W::Object&)), this, SLOT(onArtistNewElement(const W::String&, const W::Object&)));
|
||||
disconnect(artistCtrl, SIGNAL(removeElement(const W::String&)), this, SLOT(onAtristRemoveElement(const W::String&)));
|
||||
artistCtrl->deleteLater();
|
||||
}
|
||||
const W::Uint64& aid = static_cast<const W::Uint64&>(element);
|
||||
artistCtrl = new C::Vocabulary(W::Address{u"artists", W::String(aid.toString())});
|
||||
addController(artistCtrl, W::String(u"Perturabo"));
|
||||
connect(artistCtrl, SIGNAL(newElement(const W::String&, const W::Object&)), SLOT(onArtistNewElement(const W::String&, const W::Object&)));
|
||||
connect(artistCtrl, SIGNAL(removeElement(const W::String&)), SLOT(onAtristRemoveElement(const W::String&)));
|
||||
} else if (key == u"album") {
|
||||
if (albumCtrl != 0) {
|
||||
removeController(albumCtrl);
|
||||
disconnect(albumCtrl, SIGNAL(newElement(const W::String&, const W::Object&)), this, SLOT(onAlbumNewElement(const W::String&, const W::Object&)));
|
||||
disconnect(albumCtrl, SIGNAL(removeElement(const W::String&)), this, SLOT(onAlbumRemoveElement(const W::String&)));
|
||||
albumCtrl->deleteLater();
|
||||
}
|
||||
const W::Uint64& aid = static_cast<const W::Uint64&>(element);
|
||||
albumCtrl = new C::Vocabulary(W::Address{u"albums", W::String(aid.toString())});
|
||||
addController(albumCtrl, W::String(u"Perturabo"));
|
||||
connect(albumCtrl, SIGNAL(newElement(const W::String&, const W::Object&)), SLOT(onAlbumNewElement(const W::String&, const W::Object&)));
|
||||
connect(albumCtrl, SIGNAL(removeElement(const W::String&)), SLOT(onAlbumRemoveElement(const W::String&)));
|
||||
}
|
||||
}
|
||||
|
||||
void ProxySong::onSongRemoveElement(const W::String& key)
|
||||
{
|
||||
if (key == u"name") {
|
||||
insert(key, new W::String(u"undefined"));
|
||||
} else if (key == u"audio") {
|
||||
insert(key, new W::Uint64(0));
|
||||
if (_ready) {
|
||||
_ready = false;
|
||||
fileId = W::Uint64(0);
|
||||
emit notReady();
|
||||
}
|
||||
} else if (key == u"artist") {
|
||||
if (artistCtrl != 0) {
|
||||
removeController(artistCtrl);
|
||||
disconnect(artistCtrl, SIGNAL(newElement(const W::String&, const W::Object&)), this, SLOT(onArtistNewElement(const W::String&, const W::Object&)));
|
||||
disconnect(artistCtrl, SIGNAL(removeElement(const W::String&)), this, SLOT(onAtristRemoveElement(const W::String&)));
|
||||
artistCtrl->deleteLater();
|
||||
artistCtrl = 0;
|
||||
}
|
||||
} else if (key == u"album") {
|
||||
if (albumCtrl != 0) {
|
||||
removeController(albumCtrl);
|
||||
disconnect(albumCtrl, SIGNAL(newElement(const W::String&, const W::Object&)), this, SLOT(onAlbumNewElement(const W::String&, const W::Object&)));
|
||||
disconnect(albumCtrl, SIGNAL(removeElement(const W::String&)), this, SLOT(onAlbumRemoveElement(const W::String&)));
|
||||
albumCtrl->deleteLater();
|
||||
albumCtrl = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProxySong::onAlbumNewElement(const W::String& key, const W::Object& element)
|
||||
{
|
||||
if (key == u"name") {
|
||||
insert(W::String(u"album"), element);
|
||||
} else if (key == u"image") {
|
||||
insert(key, element);
|
||||
}
|
||||
}
|
||||
|
||||
void ProxySong::onAlbumRemoveElement(const W::String& key)
|
||||
{
|
||||
if (key == u"name") {
|
||||
insert(W::String(u"album"), new W::String(u"undefined"));
|
||||
} else if (key == u"image") {
|
||||
insert(key, new W::Uint64(0));
|
||||
}
|
||||
}
|
||||
|
||||
void ProxySong::onArtistNewElement(const W::String& key, const W::Object& element)
|
||||
{
|
||||
if (key == u"name") {
|
||||
insert(W::String(u"artist"), element);
|
||||
}
|
||||
}
|
||||
|
||||
void ProxySong::onArtistRemoveElement(const W::String& key)
|
||||
{
|
||||
if (key == u"name") {
|
||||
insert(W::String(u"artist"), new W::String(u"undefined"));
|
||||
}
|
||||
}
|
||||
|
||||
const W::Uint64 & ProxySong::getFileId() const
|
||||
{
|
||||
return fileId;
|
||||
}
|
47
corax/models/proxysong.h
Normal file
47
corax/models/proxysong.h
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef PROXYSONG_H
|
||||
#define PROXYSONG_H
|
||||
|
||||
/**
|
||||
* @todo write docs
|
||||
*/
|
||||
#include <wType/uint64.h>
|
||||
#include <wType/address.h>
|
||||
|
||||
#include <wModel/vocabulary.h>
|
||||
|
||||
#include <wController/vocabulary.h>
|
||||
|
||||
class ProxySong : public M::Vocabulary {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ProxySong(const W::Uint64& p_id, const W::Address& p_address, QObject* parent = 0);
|
||||
~ProxySong();
|
||||
|
||||
const W::Uint64& getFileId() const;
|
||||
bool isReady() const;
|
||||
|
||||
signals:
|
||||
void ready();
|
||||
void notReady();
|
||||
|
||||
private:
|
||||
C::Vocabulary* songCtrl;
|
||||
C::Vocabulary* albumCtrl;
|
||||
C::Vocabulary* artistCtrl;
|
||||
|
||||
W::Uint64 fileId;
|
||||
bool _ready;
|
||||
|
||||
private slots:
|
||||
void onSongNewElement(const W::String& key, const W::Object& element);
|
||||
void onSongRemoveElement(const W::String& key);
|
||||
|
||||
void onAlbumNewElement(const W::String& key, const W::Object& element);
|
||||
void onAlbumRemoveElement(const W::String& key);
|
||||
|
||||
void onArtistNewElement(const W::String& key, const W::Object& element);
|
||||
void onArtistRemoveElement(const W::String& key);
|
||||
|
||||
};
|
||||
|
||||
#endif // PROXYSONG_H
|
@ -276,3 +276,8 @@ bool C::Controller::isSubscribed()
|
||||
{
|
||||
return subscribed;
|
||||
}
|
||||
|
||||
const W::Address & C::Controller::getAddress() const
|
||||
{
|
||||
return address;
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ namespace C {
|
||||
void subscribe();
|
||||
void unsubscribe();
|
||||
bool isSubscribed();
|
||||
const W::Address& getAddress() const;
|
||||
|
||||
void removeHandler(W::Handler* handler);
|
||||
void removeController(C::Controller* ctrl);
|
||||
|
@ -175,6 +175,8 @@ void Database::clear()
|
||||
lmdb::txn transaction = lmdb::txn::begin(environment);
|
||||
dbi.drop(transaction);
|
||||
transaction.commit();
|
||||
|
||||
elements.clear();
|
||||
}
|
||||
|
||||
void Database::addModel(M::Model* model)
|
||||
|
@ -215,7 +215,7 @@ void ResourceCache::h_subscribeMember(const W::Event& ev)
|
||||
M::File* modelRecord = M::File::create(readFile(*record), address + lastHops >> 1);
|
||||
delete record;
|
||||
addModel(modelRecord);
|
||||
passToHandler(ev);
|
||||
passToLocalHandler(ev);
|
||||
} catch (int err) {
|
||||
if (err == 3) {
|
||||
emit serviceMessage(QString("An attempt to create and subscribe record model in resourcecache, but it is not found. Event: ") + ev.toString().c_str());
|
||||
|
@ -14,7 +14,9 @@ set(HEADERS
|
||||
attributes.h
|
||||
icatalogue.h
|
||||
catalogue.h
|
||||
button.h
|
||||
file/file.h
|
||||
file/audio.h
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
@ -25,12 +27,15 @@ set(SOURCES
|
||||
attributes.cpp
|
||||
icatalogue.cpp
|
||||
catalogue.cpp
|
||||
button.cpp
|
||||
file/file.cpp
|
||||
file/audio.cpp
|
||||
)
|
||||
|
||||
add_library(wModel STATIC ${HEADERS} ${SOURCES})
|
||||
|
||||
target_link_libraries(wModel Qt5::Core)
|
||||
target_link_libraries(wModel wSocket)
|
||||
target_link_libraries(wModel wDispatcher)
|
||||
target_link_libraries(wModel wServerUtils)
|
||||
target_link_libraries(wModel wType)
|
||||
target_link_libraries(wModel wController)
|
||||
target_link_libraries(wModel mad)
|
||||
|
121
lib/wModel/button.cpp
Normal file
121
lib/wModel/button.cpp
Normal file
@ -0,0 +1,121 @@
|
||||
#include "button.h"
|
||||
|
||||
M::Button::Button(const W::Address& address, QObject* parent):
|
||||
M::Model(address, parent),
|
||||
enabled(true),
|
||||
hasImage(false),
|
||||
hasLabel(false),
|
||||
imageName(0),
|
||||
label(0)
|
||||
{
|
||||
W::Handler* get = W::Handler::create(address + W::Address({u"get"}), this, &M::Button::_h_get);
|
||||
W::Handler* activate = W::Handler::create(address + W::Address({u"activate"}), this, &M::Button::_h_activate);
|
||||
|
||||
addHandler(get);
|
||||
addHandler(activate);
|
||||
}
|
||||
|
||||
M::Button::~Button()
|
||||
{
|
||||
if (hasImage) {
|
||||
delete imageName;
|
||||
}
|
||||
}
|
||||
|
||||
void M::Button::setImage(const W::String& p_image)
|
||||
{
|
||||
|
||||
if (hasImage) {
|
||||
if (*imageName != p_image) {
|
||||
imageName = static_cast<W::String*>(p_image.copy());
|
||||
if (registered) {
|
||||
W::Vocabulary* vc = new W::Vocabulary();
|
||||
vc->insert(u"image", p_image);
|
||||
broadcast(vc, W::Address{u"changeImage"});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
imageName = static_cast<W::String*>(p_image.copy());
|
||||
hasImage = true;
|
||||
if (registered) {
|
||||
W::Vocabulary* vc = new W::Vocabulary();
|
||||
vc->insert(u"image", p_image);
|
||||
broadcast(vc, W::Address{u"setImage"});
|
||||
}
|
||||
}
|
||||
hasImage = true;
|
||||
}
|
||||
|
||||
void M::Button::setEnabled(bool p_enabled)
|
||||
{
|
||||
if (enabled != p_enabled) {
|
||||
enabled = p_enabled;
|
||||
if (registered) {
|
||||
W::Vocabulary* vc = new W::Vocabulary();
|
||||
vc->insert(u"enabled", new W::Boolean(enabled));
|
||||
broadcast(vc, W::Address{u"setEnabled"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void M::Button::setLabel(const W::String& p_label)
|
||||
{
|
||||
if (hasLabel) {
|
||||
label->set(p_label);
|
||||
} else {
|
||||
label = new M::String(p_label, address + W::Address{u"label"});
|
||||
addModel(label);
|
||||
if (registered) {
|
||||
W::Vocabulary* vc = new W::Vocabulary();
|
||||
vc->insert(u"hasLabel", new W::Boolean(true));
|
||||
vc->insert(u"label", label->getAddress());
|
||||
broadcast(vc, W::Address{u"setLabel"});
|
||||
}
|
||||
hasLabel = true;
|
||||
}
|
||||
}
|
||||
|
||||
void M::Button::h_subscribe(const W::Event& ev)
|
||||
{
|
||||
M::Model::h_subscribe(ev);
|
||||
|
||||
h_get(ev);
|
||||
}
|
||||
|
||||
void M::Button::h_get(const W::Event& ev)
|
||||
{
|
||||
W::Vocabulary* vc = new W::Vocabulary();
|
||||
vc->insert(u"hasImage", new W::Boolean(hasImage));
|
||||
if (hasImage) {
|
||||
vc->insert(u"image", imageName->copy());
|
||||
}
|
||||
vc->insert(u"hasLabel", new W::Boolean(hasLabel));
|
||||
if (hasLabel) {
|
||||
vc->insert(u"label", label->getAddress());
|
||||
}
|
||||
vc->insert(u"enabled", new W::Boolean(enabled));
|
||||
|
||||
response(vc, W::Address({u"get"}), ev);
|
||||
}
|
||||
|
||||
void M::Button::h_activate(const W::Event& ev)
|
||||
{
|
||||
if (enabled) {
|
||||
emit activated();
|
||||
}
|
||||
}
|
||||
|
||||
M::Model::ModelType M::Button::getType() const
|
||||
{
|
||||
return M::Model::button;
|
||||
}
|
||||
|
||||
void M::Button::set(const W::Object& value)
|
||||
{
|
||||
throw 14; //what do you expect here? not implemented, and not sure it ever would be
|
||||
}
|
||||
|
||||
void M::Button::set(W::Object* value)
|
||||
{
|
||||
set(*value);
|
||||
}
|
52
lib/wModel/button.h
Normal file
52
lib/wModel/button.h
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef BUTTON_H
|
||||
#define BUTTON_H
|
||||
|
||||
#include <wModel/model.h>
|
||||
#include <wModel/modelstring.h>
|
||||
|
||||
#include <wDispatcher/handler.h>
|
||||
|
||||
#include <wType/address.h>
|
||||
#include <wType/event.h>
|
||||
#include <wType/vocabulary.h>
|
||||
#include <wType/boolean.h>
|
||||
|
||||
/**
|
||||
* @todo write docs
|
||||
*/
|
||||
namespace M {
|
||||
class Button : public Model {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Button(const W::Address& address, QObject* parent = 0);
|
||||
~Button();
|
||||
|
||||
void setImage(const W::String& p_image);
|
||||
void setLabel(const W::String& p_label);
|
||||
void setEnabled(bool p_enabled);
|
||||
|
||||
M::Model::ModelType getType() const override;
|
||||
void set(const W::Object & value) override;
|
||||
void set(W::Object * value) override;
|
||||
|
||||
signals:
|
||||
void activated() const;
|
||||
|
||||
protected:
|
||||
void h_subscribe(const W::Event & ev) override;
|
||||
|
||||
handler(get);
|
||||
handler(activate);
|
||||
|
||||
protected:
|
||||
bool enabled;
|
||||
bool hasImage;
|
||||
bool hasLabel;
|
||||
|
||||
W::String* imageName;
|
||||
M::String* label;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif // BUTTON_H
|
45
lib/wModel/file/audio.cpp
Normal file
45
lib/wModel/file/audio.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
#include "audio.h"
|
||||
|
||||
#include <mad.h>
|
||||
|
||||
M::Audio::Audio(W::Blob* p_file, const W::Address& addr, QObject* parent):
|
||||
File(p_file, addr, parent)
|
||||
{
|
||||
}
|
||||
|
||||
M::Audio::~Audio()
|
||||
{}
|
||||
|
||||
M::Model::ModelType M::Audio::getType() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
void M::Audio::initAdditional(const W::String& p_mime)
|
||||
{
|
||||
File::initAdditional(p_mime); //TODO handle other than mp3 formats
|
||||
|
||||
mad_stream stream;
|
||||
mad_header header;
|
||||
mad_stream_init(&stream);
|
||||
mad_header_init(&header);
|
||||
|
||||
mad_stream_buffer(&stream, file->uchar(), file->size());
|
||||
|
||||
uint64_t length = 0;
|
||||
uint64_t tBits = 0;
|
||||
uint64_t amount = 0;
|
||||
while(stream.error != MAD_ERROR_BUFLEN) { //TODO handle other errors;
|
||||
|
||||
int success = mad_header_decode(&header, &stream);
|
||||
if (success == 0) {
|
||||
|
||||
amount++;
|
||||
length += header.duration.seconds * MAD_TIMER_RESOLUTION + header.duration.fraction;
|
||||
tBits += header.bitrate;
|
||||
}
|
||||
}
|
||||
|
||||
additional.insert(u"duration", new W::Uint64(length * 1000 / MAD_TIMER_RESOLUTION));
|
||||
additional.insert(u"bitrate", new W::Uint64(tBits / amount));
|
||||
}
|
31
lib/wModel/file/audio.h
Normal file
31
lib/wModel/file/audio.h
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef M_AUDIO_H
|
||||
#define M_AUDIO_H
|
||||
|
||||
#include <wModel/file/file.h>
|
||||
|
||||
#include <wType/blob.h>
|
||||
#include <deque>
|
||||
|
||||
namespace M {
|
||||
|
||||
/**
|
||||
* @todo write docs
|
||||
*/
|
||||
class Audio : public File {
|
||||
friend class File;
|
||||
protected:
|
||||
Audio(W::Blob* p_file, const W::Address& addr, QObject* parent = 0);
|
||||
|
||||
public:
|
||||
~Audio();
|
||||
|
||||
M::Model::ModelType getType() const override;
|
||||
static const M::Model::ModelType type = audio;
|
||||
|
||||
protected:
|
||||
void initAdditional(const W::String& p_mime) override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // M_AUDIO_H
|
@ -1,7 +1,13 @@
|
||||
#include "file.h"
|
||||
#include <iostream>
|
||||
|
||||
#include "audio.h"
|
||||
|
||||
QMimeDatabase M::File::mimeDB;
|
||||
const std::map<QString, M::Model::ModelType> M::File::mimeMap = {
|
||||
{"image/jpeg", M::Model::file},
|
||||
{"audio/mpeg", M::Model::audio}
|
||||
};
|
||||
|
||||
M::File::File(W::Blob* p_file, const W::Address& addr, QObject* parent):
|
||||
M::Model(addr, parent),
|
||||
@ -10,9 +16,11 @@ M::File::File(W::Blob* p_file, const W::Address& addr, QObject* parent):
|
||||
{
|
||||
W::Handler* get = W::Handler::create(address + W::Address({u"get"}), this, &M::File::_h_get);
|
||||
W::Handler* getAdditional = W::Handler::create(address + W::Address({u"getAdditional"}), this, &M::File::_h_getAdditional);
|
||||
W::Handler* getSlice = W::Handler::create(address + W::Address({u"getSlice"}), this, &M::File::_h_getSlice);
|
||||
|
||||
addHandler(get);
|
||||
addHandler(getAdditional);
|
||||
addHandler(getSlice);
|
||||
}
|
||||
|
||||
M::File::~File()
|
||||
@ -29,7 +37,7 @@ void M::File::initAdditional(const W::String& p_mime)
|
||||
{
|
||||
additional.clear();
|
||||
|
||||
additional.insert(u"size", new W::Uint64(file->size()));
|
||||
additional.insert(u"size", new W::Uint64(file->length()));
|
||||
additional.insert(u"mimeType", p_mime);
|
||||
}
|
||||
|
||||
@ -79,8 +87,42 @@ M::File * M::File::create(W::Blob* blob, const W::Address& addr, QObject* parent
|
||||
M::File* out;
|
||||
|
||||
QMimeType mt = mimeDB.mimeTypeForData(blob->byteArray());
|
||||
out = new File(blob, addr, parent);
|
||||
|
||||
out->initAdditional(W::String(mt.name().toStdString()));
|
||||
const QString& mime = mt.name();
|
||||
std::map<QString, M::Model::ModelType>::const_iterator itr = mimeMap.find(mime);
|
||||
|
||||
M::Model::ModelType modelType = M::Model::file;
|
||||
if (itr != mimeMap.end()) {
|
||||
modelType = itr->second;
|
||||
}
|
||||
|
||||
switch (modelType) {
|
||||
case Model::audio:
|
||||
out = new Audio(blob, addr, parent);
|
||||
break;
|
||||
default:
|
||||
out = new File(blob, addr, parent);
|
||||
break;
|
||||
}
|
||||
|
||||
out->initAdditional(W::String(mime.toStdString()));
|
||||
return out;
|
||||
}
|
||||
|
||||
void M::File::h_getSlice(const W::Event& ev)
|
||||
{
|
||||
const W::Vocabulary& vc = static_cast<const W::Vocabulary&>(ev.getData());
|
||||
const W::Uint64& begin = static_cast<const W::Uint64&>(vc.at(u"begin"));
|
||||
const W::Uint64& size = static_cast<const W::Uint64&>(vc.at(u"size"));
|
||||
|
||||
W::Vocabulary* evc = new W::Vocabulary();
|
||||
if (begin > file->length() || begin + size > file->length()) {
|
||||
evc->insert(u"result", new W::Uint64(1));
|
||||
} else {
|
||||
evc->insert(u"result", new W::Uint64(0));
|
||||
evc->insert(u"slice", file->slice(begin, size));
|
||||
}
|
||||
|
||||
response(evc, W::Address{u"getSlice"}, ev);
|
||||
}
|
||||
|
||||
|
@ -32,12 +32,14 @@ namespace M {
|
||||
|
||||
handler(get);
|
||||
handler(getAdditional);
|
||||
handler(getSlice);
|
||||
|
||||
protected:
|
||||
W::Vocabulary additional;
|
||||
W::Blob* file;
|
||||
|
||||
static QMimeDatabase mimeDB;
|
||||
static const std::map<QString, M::Model::ModelType> mimeMap;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,15 @@ void M::ICatalogue::clear()
|
||||
broadcast(new W::Vocabulary(), W::Address{u"clear"});
|
||||
}
|
||||
|
||||
|
||||
std::map<uint64_t, M::Vocabulary*>::iterator aItr = activeChildren.begin();
|
||||
std::map<uint64_t, M::Vocabulary*>::iterator aEnd = activeChildren.end();
|
||||
for (; aItr != aEnd; ++aItr) {
|
||||
removeModel(aItr->second);
|
||||
aItr->second->deleteLater();
|
||||
}
|
||||
activeChildren.clear();
|
||||
|
||||
emit countChange(0);
|
||||
}
|
||||
|
||||
|
@ -5,12 +5,12 @@ M::Model::Model(const W::Address p_address, QObject* parent):
|
||||
address(p_address),
|
||||
registered(false),
|
||||
subscribers(new Map()),
|
||||
dispatcher(0),
|
||||
server(0),
|
||||
connector(0),
|
||||
subscribersCount(0),
|
||||
handlers(new HList()),
|
||||
properties(new W::Vector()),
|
||||
models(new MList())
|
||||
models(new MList()),
|
||||
controllers(new Controllers())
|
||||
{
|
||||
W::Handler* subscribe = W::Handler::create(address + W::Address({u"subscribe"}), this, &M::Model::_h_subscribe);
|
||||
W::Handler* unsubscribe = W::Handler::create(address + W::Address({u"unsubscribe"}), this, &M::Model::_h_unsubscribe);
|
||||
@ -21,7 +21,7 @@ M::Model::Model(const W::Address p_address, QObject* parent):
|
||||
M::Model::~Model()
|
||||
{
|
||||
if (registered) {
|
||||
unregisterModel();
|
||||
getUnregistered();
|
||||
}
|
||||
|
||||
MList::iterator itr = models->begin();
|
||||
@ -38,10 +38,18 @@ M::Model::~Model()
|
||||
delete *hItr;
|
||||
}
|
||||
|
||||
Controllers::iterator cItr = controllers->begin();
|
||||
Controllers::iterator cEnd = controllers->end();
|
||||
|
||||
for (; cItr != cEnd; ++cItr) {
|
||||
delete cItr->first;
|
||||
}
|
||||
|
||||
delete subscribers;
|
||||
delete properties;
|
||||
delete handlers;
|
||||
delete models;
|
||||
delete controllers;
|
||||
}
|
||||
|
||||
void M::Model::addModel(M::Model* model)
|
||||
@ -49,7 +57,7 @@ void M::Model::addModel(M::Model* model)
|
||||
models->push_back(model);
|
||||
connect(model, SIGNAL(serviceMessage(const QString&)), SIGNAL(serviceMessage(const QString&)));
|
||||
if (registered) {
|
||||
model->registerModel(dispatcher, server);
|
||||
model->getRegistered(connector);
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,7 +65,7 @@ void M::Model::addHandler(W::Handler* handler)
|
||||
{
|
||||
handlers->push_back(handler);
|
||||
if (registered) {
|
||||
dispatcher->registerHandler(handler);
|
||||
connector->registerHandler(handler);
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,21 +91,20 @@ W::Address M::Model::getAddress() const
|
||||
}
|
||||
|
||||
|
||||
void M::Model::registerModel(W::Dispatcher* dp, W::Server* srv)
|
||||
void M::Model::getRegistered(U::Connector* cn)
|
||||
{
|
||||
if (registered) {
|
||||
emit serviceMessage(QString("Model ") + address.toString().c_str() + " is already registered");
|
||||
throw 1;
|
||||
} else {
|
||||
dispatcher = dp;
|
||||
server = srv;
|
||||
connector = cn;
|
||||
|
||||
MList::iterator itr = models->begin();
|
||||
MList::iterator end = models->end();
|
||||
|
||||
for (; itr != end; ++itr) {
|
||||
M::Model* model = *itr;
|
||||
model->registerModel(dispatcher, server);
|
||||
model->getRegistered(connector);
|
||||
}
|
||||
|
||||
HList::iterator hItr = handlers->begin();
|
||||
@ -105,14 +112,21 @@ void M::Model::registerModel(W::Dispatcher* dp, W::Server* srv)
|
||||
|
||||
for (; hItr != hEnd; ++hItr) {
|
||||
W::Handler* handler = *hItr;
|
||||
dispatcher->registerHandler(handler);
|
||||
connector->registerHandler(handler);
|
||||
}
|
||||
|
||||
Controllers::iterator cItr = controllers->begin();
|
||||
Controllers::iterator cEnd = controllers->end();
|
||||
|
||||
for (; cItr != cEnd; ++cItr) {
|
||||
connector->registerController(cItr->first, cItr->second);
|
||||
}
|
||||
|
||||
registered = true;
|
||||
}
|
||||
}
|
||||
|
||||
void M::Model::unregisterModel()
|
||||
void M::Model::getUnregistered()
|
||||
{
|
||||
if (!registered) {
|
||||
emit serviceMessage(QString("Model ") + address.toString().c_str() + " is not registered");
|
||||
@ -123,7 +137,7 @@ void M::Model::unregisterModel()
|
||||
|
||||
for (; itr != end; ++itr) {
|
||||
Model* model = *itr;
|
||||
model->unregisterModel();
|
||||
model->getUnregistered();
|
||||
}
|
||||
|
||||
HList::iterator hItr = handlers->begin();
|
||||
@ -131,21 +145,27 @@ void M::Model::unregisterModel()
|
||||
|
||||
for (; hItr != hEnd; ++hItr) {
|
||||
W::Handler* handler = *hItr;
|
||||
dispatcher->unregisterHandler(handler);
|
||||
connector->unregisterHandler(handler);
|
||||
}
|
||||
|
||||
Map::iterator sItr = subscribers->begin();
|
||||
Map::iterator sEnd = subscribers->end();
|
||||
|
||||
for (; sItr != sEnd; ++sItr) {
|
||||
const W::Socket& socket = server->getConnection(sItr->first);
|
||||
disconnect(&socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
|
||||
const W::Socket* socket = connector->getConnection(sItr->first);
|
||||
disconnect(socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
|
||||
}
|
||||
subscribers->clear();
|
||||
subscribersCount = 0;
|
||||
|
||||
dispatcher = 0;
|
||||
server = 0;
|
||||
Controllers::iterator cItr = controllers->begin();
|
||||
Controllers::iterator cEnd = controllers->end();
|
||||
|
||||
for (; cItr != cEnd; ++cItr) {
|
||||
connector->unregisterController(cItr->first, cItr->second);
|
||||
}
|
||||
|
||||
connector = 0;
|
||||
|
||||
registered = false;
|
||||
}
|
||||
@ -162,7 +182,6 @@ void M::Model::h_subscribe(const W::Event& ev)
|
||||
params = static_cast<const W::Vocabulary&>(vc.at(u"params"));
|
||||
}
|
||||
|
||||
|
||||
Map::iterator sItr = subscribers->find(id);
|
||||
|
||||
if (sItr == subscribers->end()) {
|
||||
@ -171,8 +190,8 @@ void M::Model::h_subscribe(const W::Event& ev)
|
||||
emit serviceMessage(QString("Model ") + address.toString().c_str() + ": something completely wrong happened");
|
||||
throw 3;
|
||||
}
|
||||
const W::Socket& socket = server->getConnection(id);
|
||||
connect(&socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
|
||||
const W::Socket* socket = connector->getConnection(id);
|
||||
connect(socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
|
||||
sItr = pair.first;
|
||||
}
|
||||
SMap::const_iterator oItr = sItr->second.find(source);
|
||||
@ -238,8 +257,8 @@ void M::Model::h_unsubscribe(const W::Event& ev)
|
||||
|
||||
smap.erase(sItr);
|
||||
if (smap.size() == 0) {
|
||||
const W::Socket& socket = server->getConnection(itr->first);
|
||||
disconnect(&socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
|
||||
const W::Socket* socket = connector->getConnection(itr->first);
|
||||
disconnect(socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
|
||||
subscribers->erase(itr);
|
||||
}
|
||||
--subscribersCount;
|
||||
@ -256,7 +275,7 @@ void M::Model::send(W::Vocabulary* vc, const W::Address& destination, uint64_t c
|
||||
}
|
||||
W::Event ev(destination, vc);
|
||||
ev.setSenderId(connectionId);
|
||||
server->getConnection(connectionId).send(ev);
|
||||
connector->getConnection(connectionId)->send(ev);
|
||||
}
|
||||
|
||||
void M::Model::response(W::Vocabulary* vc, const W::Address& handlerAddress, const W::Event& src)
|
||||
@ -272,7 +291,7 @@ void M::Model::response(W::Vocabulary* vc, const W::Address& handlerAddress, con
|
||||
|
||||
W::Event ev(source + handlerAddress, vc);
|
||||
ev.setSenderId(id);
|
||||
server->getConnection(id).send(ev);
|
||||
connector->getConnection(id)->send(ev);
|
||||
}
|
||||
|
||||
void M::Model::fakeResponse(W::Vocabulary* vc, const W::Address& handlerAddress, const W::Address& sourceAddress, const W::Event& src)
|
||||
@ -288,7 +307,7 @@ void M::Model::fakeResponse(W::Vocabulary* vc, const W::Address& handlerAddress,
|
||||
|
||||
W::Event ev(source + handlerAddress, vc);
|
||||
ev.setSenderId(id);
|
||||
server->getConnection(id).send(ev);
|
||||
connector->getConnection(id)->send(ev);
|
||||
}
|
||||
|
||||
void M::Model::broadcast(W::Vocabulary* vc, const W::Address& handlerAddress)
|
||||
@ -307,7 +326,7 @@ void M::Model::broadcast(W::Vocabulary* vc, const W::Address& handlerAddress)
|
||||
for (;oItr != oEnd; ++oItr) {
|
||||
W::Event ev(oItr->first + handlerAddress, vc->copy());
|
||||
ev.setSenderId(itr->first);
|
||||
server->getConnection(itr->first).send(ev);
|
||||
connector->getConnection(itr->first)->send(ev);
|
||||
}
|
||||
}
|
||||
delete vc;
|
||||
@ -317,23 +336,54 @@ void M::Model::removeHandler(W::Handler* handler)
|
||||
{
|
||||
handlers->erase(handler);
|
||||
if (registered) {
|
||||
dispatcher->unregisterHandler(handler);
|
||||
connector->unregisterHandler(handler);
|
||||
}
|
||||
}
|
||||
|
||||
void M::Model::removeModel(M::Model* model)
|
||||
{
|
||||
models->erase(model);
|
||||
disconnect(model, SIGNAL(serviceMessage(const QString&)), this, SIGNAL(serviceMessage(const QString&)));
|
||||
if (registered) {
|
||||
model->unregisterModel();
|
||||
model->getUnregistered();
|
||||
}
|
||||
}
|
||||
|
||||
void M::Model::passToHandler(const W::Event& event) const
|
||||
void M::Model::passToLocalHandler(const W::Event& event) const
|
||||
{
|
||||
if (registered) {
|
||||
dispatcher->pass(event);
|
||||
connector->passThroughDispatcher(event);
|
||||
} else {
|
||||
emit serviceMessage(QString("An attempt to pass event to dispatcher from unregistered model\nModel address ") + address.toString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void M::Model::addController(C::Controller* ctrl, const W::String& nodeName)
|
||||
{
|
||||
Controllers::const_iterator itr = controllers->find(ctrl);
|
||||
if (itr != controllers->end()) {
|
||||
emit serviceMessage(QString("An attempt to add controller ") + ctrl->getAddress().toString().c_str() + QString(" for the second time in model ") + address.toString().c_str());
|
||||
throw 9;
|
||||
}
|
||||
controllers->insert(std::make_pair(ctrl, nodeName));
|
||||
connect(ctrl, SIGNAL(serviceMessage(const QString&)), SIGNAL(serviceMessage(const QString&)));
|
||||
if (registered) {
|
||||
connector->registerController(ctrl, nodeName);;
|
||||
}
|
||||
}
|
||||
|
||||
void M::Model::removeController(C::Controller* ctrl)
|
||||
{
|
||||
Controllers::const_iterator itr = controllers->find(ctrl);
|
||||
if (itr == controllers->end()) {
|
||||
emit serviceMessage(QString("An attempt to remove absent controller ") + ctrl->getAddress().toString().c_str() + QString(" from the model ") + address.toString().c_str());
|
||||
throw 10;
|
||||
}
|
||||
|
||||
disconnect(ctrl, SIGNAL(serviceMessage(const QString&)), this, SIGNAL(serviceMessage(const QString&)));
|
||||
if (registered) {
|
||||
connector->unregisterController(itr->first, itr->second);
|
||||
}
|
||||
controllers->erase(itr);
|
||||
}
|
||||
|
||||
|
@ -13,10 +13,10 @@
|
||||
#include <wType/event.h>
|
||||
#include <wType/string.h>
|
||||
#include <wSocket/socket.h>
|
||||
#include <wSocket/server.h>
|
||||
#include <wDispatcher/dispatcher.h>
|
||||
#include <wDispatcher/handler.h>
|
||||
#include <wContainer/order.h>
|
||||
#include <wServerUtils/connector.h>
|
||||
#include <wController/controller.h>
|
||||
|
||||
namespace M {
|
||||
|
||||
@ -29,10 +29,16 @@ namespace M {
|
||||
list,
|
||||
vocabulary,
|
||||
catalogue,
|
||||
image,
|
||||
button,
|
||||
model,
|
||||
|
||||
attributes = 50,
|
||||
file,
|
||||
resourceCache
|
||||
resourceCache,
|
||||
audio,
|
||||
|
||||
player = 107
|
||||
};
|
||||
|
||||
Model(const W::Address p_address, QObject* parent = 0);
|
||||
@ -46,13 +52,15 @@ namespace M {
|
||||
void addModel(M::Model* model);
|
||||
void addHandler(W::Handler* handler);
|
||||
void addProperty(const W::String& value, const W::String& name);
|
||||
void addController(C::Controller* ctrl, const W::String& nodeName);
|
||||
W::Address getAddress() const;
|
||||
void registerModel(W::Dispatcher* dp, W::Server* srv);
|
||||
void unregisterModel();
|
||||
void getRegistered(U::Connector* connector);
|
||||
void getUnregistered();
|
||||
|
||||
void removeHandler(W::Handler* handler);
|
||||
void removeModel(M::Model* model);
|
||||
void passToHandler(const W::Event& event) const;
|
||||
void removeController(C::Controller* ctrl);
|
||||
void passToLocalHandler(const W::Event& event) const;
|
||||
|
||||
signals:
|
||||
void serviceMessage(const QString& msg) const;
|
||||
@ -76,13 +84,14 @@ namespace M {
|
||||
private:
|
||||
typedef W::Order<W::Handler*> HList;
|
||||
typedef W::Order<M::Model*> MList;
|
||||
typedef std::map<C::Controller*, W::String> Controllers;
|
||||
|
||||
W::Dispatcher* dispatcher;
|
||||
W::Server* server;
|
||||
U::Connector* connector;
|
||||
uint64_t subscribersCount;
|
||||
HList* handlers;
|
||||
W::Vector* properties;
|
||||
MList* models;
|
||||
Controllers* controllers;
|
||||
|
||||
private slots:
|
||||
void onSocketDisconnected();
|
||||
|
@ -12,8 +12,7 @@
|
||||
namespace M {
|
||||
class ICatalogue;
|
||||
|
||||
class Vocabulary : public M::Model
|
||||
{
|
||||
class Vocabulary : public M::Model {
|
||||
friend class ICatalogue;
|
||||
public:
|
||||
Vocabulary(const W::Address p_address, QObject* parent = 0);
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "connector.h"
|
||||
#include "commands.h"
|
||||
|
||||
U::Connector::Connector(W::Dispatcher* dp, W::Server* srv, U::Commands* cmds, QObject* parent):
|
||||
QObject(parent),
|
||||
@ -6,7 +7,8 @@ U::Connector::Connector(W::Dispatcher* dp, W::Server* srv, U::Commands* cmds, QO
|
||||
server(srv),
|
||||
commands(cmds),
|
||||
nodes(),
|
||||
ignoredNodes()
|
||||
ignoredNodes(),
|
||||
controllers()
|
||||
{
|
||||
connect(server, SIGNAL(newConnection(const W::Socket&)), SLOT(onNewConnection(const W::Socket&)));
|
||||
connect(server, SIGNAL(closedConnection(const W::Socket&)), SLOT(onClosedConnection(const W::Socket&)));
|
||||
@ -30,6 +32,17 @@ U::Connector::~Connector()
|
||||
for (; itr != end; ++itr) {
|
||||
commands->removeCommand(dc + itr->first);
|
||||
}
|
||||
|
||||
std::multimap<W::String, C::Controller*>::const_iterator cbeg = controllers.begin();
|
||||
std::multimap<W::String, C::Controller*>::const_iterator cend = controllers.end();
|
||||
|
||||
for (; cbeg != cend; ++cbeg) {
|
||||
C::Controller* ctrl = cbeg->second;
|
||||
if (ctrl->isSubscribed()) {
|
||||
ctrl->unsubscribe();
|
||||
ctrl->unregisterController();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void U::Connector::addIgnoredNode(const W::String& name)
|
||||
@ -43,7 +56,7 @@ void U::Connector::sendTo(const W::String& name, const W::Event& event)
|
||||
if (itr != nodes.end()) {
|
||||
throw new NodeAccessError(name);
|
||||
} else {
|
||||
server->getConnection(itr->second).send(event);
|
||||
server->getConnection(itr->second)->send(event);
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,6 +82,14 @@ void U::Connector::onNewConnection(const W::Socket& socket)
|
||||
emit serviceMessage(QString("New connection, id: ") + socket.getId().toString().c_str());
|
||||
connect(&socket, SIGNAL(message(const W::Event&)), dispatcher, SLOT(pass(const W::Event&)));
|
||||
|
||||
std::multimap<W::String, C::Controller*>::const_iterator beg = controllers.lower_bound(name);
|
||||
std::multimap<W::String, C::Controller*>::const_iterator end = controllers.upper_bound(name);
|
||||
|
||||
for (; beg != end; ++beg) {
|
||||
beg->second->registerController(dispatcher, &socket);
|
||||
beg->second->subscribe();
|
||||
}
|
||||
|
||||
emit nodeConnected(name);
|
||||
}
|
||||
} else {
|
||||
@ -90,6 +111,14 @@ void U::Connector::onClosedConnection(const W::Socket& socket)
|
||||
if (ign == ignoredNodes.end()) {
|
||||
Map::const_iterator itr = nodes.find(name);
|
||||
if (itr != nodes.end()) {
|
||||
std::multimap<W::String, C::Controller*>::const_iterator beg = controllers.lower_bound(name);
|
||||
std::multimap<W::String, C::Controller*>::const_iterator end = controllers.upper_bound(name);
|
||||
|
||||
for (; beg != end; ++beg) {
|
||||
beg->second->unsubscribe();
|
||||
beg->second->unregisterController();
|
||||
}
|
||||
|
||||
emit nodeDisconnected(name);
|
||||
commands->removeCommand(W::String(u"disconnect") + name);
|
||||
nodes.erase(itr);
|
||||
@ -114,7 +143,7 @@ void U::Connector::h_disconnect(const W::Event& ev)
|
||||
server->closeConnection(itr->second);
|
||||
}
|
||||
|
||||
const W::Socket& U::Connector::getNodeSocket(const W::String& name)
|
||||
const W::Socket* U::Connector::getNodeSocket(const W::String& name)
|
||||
{
|
||||
Map::const_iterator itr = nodes.find(name);
|
||||
if (itr == nodes.end()) {
|
||||
@ -122,3 +151,62 @@ const W::Socket& U::Connector::getNodeSocket(const W::String& name)
|
||||
}
|
||||
return server->getConnection(itr->second);
|
||||
}
|
||||
|
||||
void U::Connector::registerHandler(W::Handler* handler)
|
||||
{
|
||||
dispatcher->registerHandler(handler);
|
||||
}
|
||||
|
||||
void U::Connector::unregisterHandler(W::Handler* handler)
|
||||
{
|
||||
dispatcher->unregisterHandler(handler);
|
||||
}
|
||||
|
||||
const W::Socket * U::Connector::getConnection(uint64_t p_id) const
|
||||
{
|
||||
return server->getConnection(p_id);
|
||||
}
|
||||
|
||||
void U::Connector::passThroughDispatcher(const W::Event& ev) const
|
||||
{
|
||||
dispatcher->pass(ev);
|
||||
}
|
||||
|
||||
void U::Connector::registerController(C::Controller* ctrl, const W::String& node)
|
||||
{
|
||||
std::set<W::String>::const_iterator iitr = ignoredNodes.find(node);
|
||||
if (iitr != ignoredNodes.end()) {
|
||||
throw 3; //this means you're trying to receive something from one of ignored nodes, which never going to be handled in connector, most probably it's a mistake
|
||||
}
|
||||
controllers.insert(std::make_pair(node, ctrl));
|
||||
Map::const_iterator itr = nodes.find(node);
|
||||
if (itr != nodes.end()) {
|
||||
ctrl->registerController(dispatcher, server->getConnection(itr->second));
|
||||
ctrl->subscribe(); //let's say I always need them subscribed, for now at least
|
||||
}
|
||||
}
|
||||
|
||||
void U::Connector::unregisterController(C::Controller* ctrl, const W::String& node)
|
||||
{
|
||||
bool found = false;
|
||||
std::multimap<W::String, C::Controller*>::const_iterator beg = controllers.lower_bound(node);
|
||||
std::multimap<W::String, C::Controller*>::const_iterator end = controllers.upper_bound(node);
|
||||
|
||||
for (; beg != end; ++beg) {
|
||||
if (beg->second == ctrl) {
|
||||
found = true; //TODO make a proper way to store 'em
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
throw 4;
|
||||
}
|
||||
|
||||
if (ctrl->isSubscribed()) {
|
||||
ctrl->unsubscribe();
|
||||
ctrl->unregisterController();
|
||||
}
|
||||
controllers.erase(beg);
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,14 @@
|
||||
#ifndef CONNECTOR_H
|
||||
#define CONNECTOR_H
|
||||
|
||||
#include <utils/defines.h>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include <wDispatcher/dispatcher.h>
|
||||
#include <wDispatcher/handler.h>
|
||||
|
||||
#include <wSocket/socket.h>
|
||||
#include <wSocket/server.h>
|
||||
@ -16,9 +19,11 @@
|
||||
|
||||
#include <utils/exception.h>
|
||||
|
||||
#include "commands.h"
|
||||
#include <wController/controller.h>
|
||||
|
||||
namespace U {
|
||||
class Commands;
|
||||
|
||||
class Connector : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -29,7 +34,13 @@ namespace U {
|
||||
|
||||
void addIgnoredNode(const W::String& name);
|
||||
void sendTo(const W::String& name, const W::Event& event);
|
||||
const W::Socket& getNodeSocket(const W::String& name);
|
||||
const W::Socket* getNodeSocket(const W::String& name);
|
||||
void registerHandler(W::Handler* handler);
|
||||
void unregisterHandler(W::Handler* handler);
|
||||
const W::Socket* getConnection(uint64_t p_id) const;
|
||||
void passThroughDispatcher(const W::Event& ev) const;
|
||||
void registerController(C::Controller* ctrl, const W::String& node);
|
||||
void unregisterController(C::Controller* ctrl, const W::String& node);
|
||||
|
||||
signals:
|
||||
void serviceMessage(const QString& msg);
|
||||
@ -42,6 +53,7 @@ namespace U {
|
||||
U::Commands* commands;
|
||||
Map nodes;
|
||||
std::set<W::String> ignoredNodes;
|
||||
std::multimap<W::String, C::Controller*> controllers;
|
||||
|
||||
protected:
|
||||
handler(connect);
|
||||
|
@ -44,14 +44,14 @@ void W::Server::stop()
|
||||
}
|
||||
}
|
||||
|
||||
const W::Socket& W::Server::getConnection(uint64_t p_id) const
|
||||
const W::Socket* W::Server::getConnection(uint64_t p_id) const
|
||||
{
|
||||
std::map<uint64_t, Socket*>::const_iterator itr = connections.find(p_id);
|
||||
if (itr == connections.end()) {
|
||||
throw new SocketAccessError();
|
||||
}
|
||||
|
||||
return *(itr->second);
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
uint64_t W::Server::getConnectionsCount() const
|
||||
|
@ -27,7 +27,7 @@ namespace W
|
||||
void listen(uint16_t port);
|
||||
void stop();
|
||||
|
||||
const Socket& getConnection(uint64_t p_id) const;
|
||||
const Socket* getConnection(uint64_t p_id) const;
|
||||
uint64_t getConnectionsCount() const;
|
||||
void closeConnection(uint64_t p_id);
|
||||
void openConnection(const String& addr, const Uint64& port);
|
||||
|
@ -20,4 +20,4 @@ add_library(wSsh ${HEADERS} ${SOURCES})
|
||||
|
||||
target_link_libraries(wSsh Qt5::Core)
|
||||
target_link_libraries(wSsh ssh)
|
||||
target_link_libraries(wSsh ssh_threads)
|
||||
#target_link_libraries(wSsh ssh_threads)
|
||||
|
@ -144,3 +144,17 @@ const QByteArray & W::Blob::byteArray() const
|
||||
return qDataView;
|
||||
}
|
||||
|
||||
const unsigned char * W::Blob::uchar() const
|
||||
{
|
||||
return (unsigned char*) data;
|
||||
}
|
||||
|
||||
W::Blob* W::Blob::slice(uint64_t start, uint64_t length) const
|
||||
{
|
||||
char* n_data = new char[length];
|
||||
for (int i = 0; i < length; ++i) {
|
||||
n_data[i] = data[start + i];
|
||||
}
|
||||
|
||||
return new W::Blob(length, n_data);
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ namespace W
|
||||
size_type size() const override;
|
||||
|
||||
objectType getType() const override;
|
||||
Blob* slice(uint64_t start, uint64_t length = 0) const;
|
||||
|
||||
bool operator==(const W::Object & other) const override;
|
||||
|
||||
@ -33,6 +34,7 @@ namespace W
|
||||
static const objectType type = blob;
|
||||
|
||||
const QByteArray& byteArray() const;
|
||||
const unsigned char* uchar() const;
|
||||
|
||||
protected:
|
||||
bool hasData;
|
||||
|
@ -102,5 +102,7 @@ W::Object::StdStr W::Object::getTypeName(W::Object::objectType type)
|
||||
case blob:
|
||||
return "Blob";
|
||||
}
|
||||
|
||||
throw 5;
|
||||
}
|
||||
|
||||
|
@ -3,3 +3,5 @@ cmake_minimum_required(VERSION 2.8.12)
|
||||
configure_file(class.js class.js)
|
||||
configure_file(subscribable.js subscribable.js)
|
||||
configure_file(globalMethods.js globalMethods.js)
|
||||
configure_file(enum.js enum.js)
|
||||
configure_file(stateMachine.js stateMachine)
|
||||
|
69
libjs/utils/enum.js
Normal file
69
libjs/utils/enum.js
Normal file
@ -0,0 +1,69 @@
|
||||
"use strict";
|
||||
|
||||
var Class = require("./class");
|
||||
|
||||
var Enum = Class.inherit({
|
||||
className: "Enum",
|
||||
constructor: function(name, additional) {
|
||||
if (typeof name !== "string" || name.length === 0) {
|
||||
throw new Error("An attempt to register enum with wrong or empty name");
|
||||
}
|
||||
|
||||
if (storage[name]) {
|
||||
throw new Error("An attempt to register enum " + name + " for the second time");
|
||||
}
|
||||
|
||||
Class.fn.constructor.call(this);
|
||||
|
||||
this.name = name;
|
||||
|
||||
this.straight = Object.create(null);
|
||||
this.reversed = Object.create(null);
|
||||
|
||||
this.additional = Object.create(null);
|
||||
this._additionals = additional || [];
|
||||
|
||||
this._lastId = -1;
|
||||
|
||||
storage[name] = this;
|
||||
},
|
||||
add: function(name, additional, id) {
|
||||
if (typeof name !== "string" || name.length === 0) {
|
||||
throw new Error("An attempt to add an entry with invalid name to enum " + name);
|
||||
}
|
||||
|
||||
if (!id) {
|
||||
id = this._lastId + 1;
|
||||
}
|
||||
if (this.straight[id] !== undefined) {
|
||||
throw new Error("Id duplication in enum " + this.name + " during an attempt to add entry " + name);
|
||||
}
|
||||
|
||||
if (this.reversed[name] !== undefined) {
|
||||
throw new Error("Name duplication in enum " + this.name + " during an attempt to add entry " + name);
|
||||
}
|
||||
|
||||
this.straight[name] = id;
|
||||
this.reversed[id] = name;
|
||||
this.additional[id] = Object.create(null);
|
||||
|
||||
for (var i = 0; i < this._additionals.length; ++i) {
|
||||
var key = this._additionals[i];
|
||||
var aVal = additional[key];
|
||||
if (aVal === undefined) {
|
||||
throw new Error("An attempt to add an entry " + name + " into enum " + this.name + " without providing additional value " + key);
|
||||
}
|
||||
this.additional[id][key] = aVal;
|
||||
}
|
||||
|
||||
this._lastId = id;
|
||||
},
|
||||
hasAdditional: function(name) {
|
||||
return this._additionals.indexOf(name) !== -1;
|
||||
}
|
||||
});
|
||||
|
||||
var storage = Object.create(null);
|
||||
Enum.storage = storage;
|
||||
|
||||
module.exports = Enum;
|
@ -65,5 +65,40 @@ global.W = {
|
||||
|
||||
// Return the modified object
|
||||
return lTarget;
|
||||
},
|
||||
touchToMouse: function (e) {
|
||||
e.preventDefault();
|
||||
if (e.touches.length > 1 || (e.type == "touchend" && e.touches.length > 0))
|
||||
return;
|
||||
|
||||
var type = null;
|
||||
var touch = null;
|
||||
var src = e.currentTarget;
|
||||
switch (e.type) {
|
||||
case "touchstart":
|
||||
type = "mousedown";
|
||||
touch = e.changedTouches[0];
|
||||
|
||||
break;
|
||||
case "touchmove":
|
||||
type = "mousemove";
|
||||
touch = e.changedTouches[0];
|
||||
src = window;
|
||||
break;
|
||||
case "touchend":
|
||||
type = "mouseup";
|
||||
touch = e.changedTouches[0];
|
||||
src = window;
|
||||
break;
|
||||
}
|
||||
|
||||
var event = new MouseEvent(type, {
|
||||
button: 0,
|
||||
screenX: touch.screenX,
|
||||
screenY: touch.screenY,
|
||||
clientX: touch.clientX,
|
||||
clientY: touch.clientY
|
||||
});
|
||||
src.dispatchEvent(event);
|
||||
}
|
||||
};
|
||||
|
33
libjs/utils/stateMachine.js
Normal file
33
libjs/utils/stateMachine.js
Normal file
@ -0,0 +1,33 @@
|
||||
"use strict";
|
||||
|
||||
var Subscribable = require("./subscribable");
|
||||
|
||||
var StateMachine = Subscribable.inherit({
|
||||
className: "StateMachine",
|
||||
constructor: function (initialState, graph) {
|
||||
Subscribable.fn.constructor.call(this);
|
||||
|
||||
this._state = initialState;
|
||||
this._graph = graph;
|
||||
},
|
||||
manipulation: function (name) {
|
||||
var newState = this._graph[this._state][name];
|
||||
if (newState) {
|
||||
var oldState = this._state;
|
||||
this._state = newState;
|
||||
|
||||
this.trigger("stateChanged", {
|
||||
newState: newState,
|
||||
manipulation: name,
|
||||
oldState: oldState
|
||||
});
|
||||
} else {
|
||||
this.trigger("stateMissed");
|
||||
}
|
||||
},
|
||||
state: function () {
|
||||
return this._state;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = StateMachine;
|
@ -17,3 +17,6 @@ configure_file(localModel.js localModel.js)
|
||||
configure_file(catalogue.js catalogue.js)
|
||||
configure_file(imagePane.js imagePane.js)
|
||||
configure_file(file/file.js file/file.js)
|
||||
configure_file(file/audio.js file/audio.js)
|
||||
configure_file(player.js player.js)
|
||||
configure_file(imageById.js imageById.js)
|
||||
|
71
libjs/wController/button.js
Normal file
71
libjs/wController/button.js
Normal file
@ -0,0 +1,71 @@
|
||||
"use strict";
|
||||
|
||||
var Controller = require("./controller");
|
||||
var String = require("./string");
|
||||
var Vocabulary = require("../wType/vocabulary");
|
||||
|
||||
var Button = Controller.inherit({
|
||||
"className": "Button",
|
||||
"constructor": function(addr) {
|
||||
Controller.fn.constructor.call(this, addr);
|
||||
|
||||
this.enabled = false;
|
||||
this.hasLabel = false;
|
||||
|
||||
this.addHandler("get");
|
||||
this.addHandler("setLabel");
|
||||
this.addHandler("setImage");
|
||||
this.addHandler("setEnabled");
|
||||
this.addHandler("changeImage");
|
||||
},
|
||||
"destructor": function() {
|
||||
|
||||
Controller.fn.destructor.call(this);
|
||||
},
|
||||
"activate": function() {
|
||||
if (this.enabled) {
|
||||
this.send(new Vocabulary, "activate");
|
||||
}
|
||||
},
|
||||
"_h_changeImage": function(ev) {
|
||||
|
||||
},
|
||||
"_h_get": function(ev) {
|
||||
this._h_setLabel(ev);
|
||||
this._h_setImage(ev);
|
||||
this._h_setEnabled(ev);
|
||||
|
||||
this.initialized = true;
|
||||
},
|
||||
"_h_setEnabled": function(ev) {
|
||||
var data = ev.getData();
|
||||
|
||||
var enabled = data.at("enabled").valueOf();
|
||||
if (this.enabled !== enabled) {
|
||||
this.enabled = enabled;
|
||||
this.trigger("setEnabled", this.enabled);
|
||||
}
|
||||
},
|
||||
"_h_setLabel": function(ev) {
|
||||
var data = ev.getData();
|
||||
var hasLabel = data.at("hasLabel").valueOf();
|
||||
|
||||
if (hasLabel !== this.hasLabel) {
|
||||
this.hasLabel = hasLabel;
|
||||
if (hasLabel) {
|
||||
this.label = new String(data.at("label").clone());
|
||||
this.addController(this.label);
|
||||
this.trigger("setLabel", true, this.label);
|
||||
} else {
|
||||
this.trigger("setLabel", false);
|
||||
this.removeController(this.label);
|
||||
this.label.destructor();
|
||||
}
|
||||
}
|
||||
},
|
||||
"_h_setImage": function(ev) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Button;
|
@ -188,7 +188,7 @@ var Controller = Subscribable.inherit({
|
||||
}
|
||||
|
||||
if (this._registered) {
|
||||
global.registerForeignController(pair.n, pair.c);
|
||||
global.unregisterForeignController(pair.n, pair.c);
|
||||
}
|
||||
|
||||
pair.c.off("serviceMessage", this._onControllerServiceMessage, this);
|
||||
@ -328,8 +328,10 @@ Controller.ModelType = {
|
||||
String: 0,
|
||||
List: 1,
|
||||
Vocabulary: 2,
|
||||
Image: 3,
|
||||
Controller: 4,
|
||||
Catalogue: 3,
|
||||
Image: 4,
|
||||
Button: 5,
|
||||
Controller: 6,
|
||||
|
||||
Attributes: 50,
|
||||
|
||||
@ -339,15 +341,18 @@ Controller.ModelType = {
|
||||
PageStorage: 103,
|
||||
PanesList: 104,
|
||||
Theme: 105,
|
||||
ThemeStorage: 106
|
||||
ThemeStorage: 106,
|
||||
Player: 107
|
||||
};
|
||||
|
||||
Controller.ReversedModelType = {
|
||||
"0": "String",
|
||||
"1": "List",
|
||||
"2": "Vocabulary",
|
||||
"3": "Image",
|
||||
"4": "Controller",
|
||||
"3": "Catalogue",
|
||||
"4": "Image",
|
||||
"5": "Button",
|
||||
"6": "Controller",
|
||||
|
||||
"50": "Attributes",
|
||||
|
||||
@ -357,7 +362,8 @@ Controller.ReversedModelType = {
|
||||
"103": "PageStorage",
|
||||
"104": "PanesList",
|
||||
"105": "Theme",
|
||||
"106": "ThemeStorage"
|
||||
"106": "ThemeStorage",
|
||||
"107": "Player"
|
||||
};
|
||||
|
||||
Controller.ModelTypesPaths = {
|
||||
@ -372,7 +378,10 @@ Controller.ModelTypesPaths = {
|
||||
PanesList: "./panesList", //resolve as dependency
|
||||
Theme: "./theme", //resolve as dependency
|
||||
ThemeStorage: "./themeStorage", //resolve as dependency
|
||||
Image: "./image" //resolve as dependency
|
||||
Image: "./image", //resolve as dependency
|
||||
Button: "./button", //resolve as dependency
|
||||
Catalogue: "./catalogue", //resolve as dependency
|
||||
Player: "./player" //resolve as dependency
|
||||
};
|
||||
|
||||
Controller.constructors = {
|
||||
|
24
libjs/wController/file/audio.js
Normal file
24
libjs/wController/file/audio.js
Normal file
@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
|
||||
var File = require("./file");
|
||||
var Vocabulary = require("../../wType/vocabulary");
|
||||
var Vector = require("../../wType/vector");
|
||||
var Uint64 = require("../../wType/uint64");
|
||||
|
||||
var Audio = File.inherit({
|
||||
className: "Audio",
|
||||
constructor: function Audio(addr) {
|
||||
File.fn.constructor.call(this, addr);
|
||||
},
|
||||
hasMore: function() {
|
||||
return this.getSize() > this.data.length();
|
||||
},
|
||||
getBitrate: function() {
|
||||
return this._additional.at("bitrate").valueOf();
|
||||
},
|
||||
getDuration: function() {
|
||||
return this._additional.at("duration").valueOf() / 1000;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Audio;
|
@ -2,6 +2,8 @@
|
||||
|
||||
var Controller = require("../controller");
|
||||
var WVocabulary = require("../../wType/vocabulary");
|
||||
var Uint64 = require("../../wType/uint64");
|
||||
var Blob = require("../../wType/blob");
|
||||
|
||||
var File = Controller.inherit({
|
||||
"className": "File",
|
||||
@ -10,17 +12,17 @@ var File = Controller.inherit({
|
||||
|
||||
this._hasData = false;
|
||||
this._hasAdditional = false;
|
||||
this.data = null;
|
||||
this.data = new Blob();
|
||||
this._additional = null;
|
||||
this._waitingForSlice = false;
|
||||
this._need = 0;
|
||||
|
||||
this.addHandler("get");
|
||||
this.addHandler("getAdditional");
|
||||
this.addHandler("getSlice");
|
||||
},
|
||||
"destructor": function() {
|
||||
if (this._hasData) {
|
||||
this.data.destructor();
|
||||
}
|
||||
this.data.destructor();
|
||||
if (this._hasAdditional) {
|
||||
this._additional.destructor();
|
||||
}
|
||||
@ -46,6 +48,9 @@ var File = Controller.inherit({
|
||||
"getMimeType": function() {
|
||||
return this._additional.at("mimeType").toString();
|
||||
},
|
||||
"getSize": function() {
|
||||
return this._additional.at("size").valueOf();
|
||||
},
|
||||
"_h_get": function(ev) {
|
||||
var dt = ev.getData();
|
||||
|
||||
@ -55,7 +60,9 @@ var File = Controller.inherit({
|
||||
}
|
||||
|
||||
this._hasData = true;
|
||||
var oldData = this.data;
|
||||
this.data = dt.at("data").clone();
|
||||
oldData.destructor();
|
||||
this.trigger("data");
|
||||
},
|
||||
"_h_getAdditional": function(ev) {
|
||||
@ -63,6 +70,29 @@ var File = Controller.inherit({
|
||||
if (ac) {
|
||||
this.trigger("additionalChange");
|
||||
}
|
||||
|
||||
if (!this.initialized) {
|
||||
this.initialized = true;
|
||||
this.trigger("ready");
|
||||
}
|
||||
},
|
||||
"_h_getSlice": function(ev) {
|
||||
if (this._waitingForSlice) {
|
||||
this._waitingForSlice = false;
|
||||
var vc = ev.getData();
|
||||
if (vc.at("result").valueOf() == 0) {
|
||||
var slice = vc.at("slice");
|
||||
this.data["+="](slice);
|
||||
this.trigger("slice", slice);
|
||||
|
||||
if (this.getSize() === this.data.length()) {
|
||||
this._hasData = true;
|
||||
this.trigger("data");
|
||||
}
|
||||
} else {
|
||||
this.trigger("serviceMessage", "Error receiving slice from " + this._pairAddress.toString(), 1);
|
||||
}
|
||||
}
|
||||
},
|
||||
"needData": function() {
|
||||
if (this._need === 0) {
|
||||
@ -72,6 +102,30 @@ var File = Controller.inherit({
|
||||
}
|
||||
++this._need;
|
||||
},
|
||||
"requestSlice": function(size) {
|
||||
if (this._hasAdditional) {
|
||||
if (this._waitingForSlice) {
|
||||
throw new Error("An attempt to request a slice of data from " + this._pairAddress.toString() + " while another request is in progress");
|
||||
}
|
||||
var begin = this.data.length();
|
||||
var newSize = Math.min(size, this.getSize() - begin);
|
||||
if (newSize !== size) {
|
||||
//TODO may be inform the developer about that?
|
||||
}
|
||||
if (newSize === 0) {
|
||||
return; //TODO may be inform the developer about that?
|
||||
}
|
||||
var vc = new WVocabulary();
|
||||
vc.insert("begin", new Uint64(begin));
|
||||
vc.insert("size", new Uint64(newSize));
|
||||
|
||||
this.send(vc, "getSlice");
|
||||
this._waitingForSlice = true;
|
||||
|
||||
} else {
|
||||
throw new Error("An attempt to request a slice of data from " + this._pairAddress.toString() + " before controller got initialized");
|
||||
}
|
||||
},
|
||||
"subscribe": function() {
|
||||
Controller.fn.subscribe.call(this);
|
||||
|
||||
|
46
libjs/wController/imageById.js
Normal file
46
libjs/wController/imageById.js
Normal file
@ -0,0 +1,46 @@
|
||||
"use strict";
|
||||
|
||||
var LocalModel = require("./localModel");
|
||||
var File = require("./file/file");
|
||||
|
||||
var Address = require("../wType/address");
|
||||
|
||||
var ImageById = LocalModel.inherit({
|
||||
className: "ImageById",
|
||||
constructor: function(properties, id) {
|
||||
LocalModel.fn.constructor.call(this, properties);
|
||||
|
||||
this._imageId = id;
|
||||
this._fileCtrl = new File(new Address(["images", this._imageId.toString()]));
|
||||
this._need = 0;
|
||||
|
||||
this._fileCtrl.on("data", this._onControllerData, this);
|
||||
|
||||
this.addForeignController("Corax", this._fileCtrl);
|
||||
},
|
||||
dontNeedData: function() {
|
||||
--this._need;
|
||||
if (this._need === 0) {
|
||||
this._fileCtrl.dontNeedData();
|
||||
}
|
||||
},
|
||||
getMimeType: function () {
|
||||
return this._fileCtrl.getMimeType();
|
||||
},
|
||||
hasData: function() {
|
||||
return this._fileCtrl.hasData();
|
||||
},
|
||||
needData: function() {
|
||||
if (this._need === 0) {
|
||||
this._fileCtrl.needData();
|
||||
}
|
||||
++this._need;
|
||||
},
|
||||
_onControllerData: function() {
|
||||
this.data = this._fileCtrl.data;
|
||||
|
||||
this.trigger("data");
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = ImageById;
|
@ -4,26 +4,97 @@ var Address = require("../wType/address");
|
||||
|
||||
var Vocabulary = require("./vocabulary");
|
||||
var File = require("./file/file");
|
||||
var LM = require("./localModel");
|
||||
|
||||
var ImagePane = Vocabulary.inherit({
|
||||
"className": "ImagePane",
|
||||
"constructor": function(addr) {
|
||||
Vocabulary.fn.constructor.call(this, addr);
|
||||
|
||||
this._actions = [];
|
||||
this._hasPageLink = false;
|
||||
this._hasImage = false;
|
||||
this.image = null;
|
||||
},
|
||||
"addElement": function(key, element) {
|
||||
if (key === "image" && !this._hasImage) {
|
||||
this._hasImage = true;
|
||||
this.image = new File(new Address(["images", element.toString()]));
|
||||
this.addForeignController("Corax", this.image);
|
||||
"_actionActivate": function(name) {
|
||||
var actObj = standardActions[name];
|
||||
|
||||
actObj.handler(this);
|
||||
},
|
||||
"addAction": function(action) {
|
||||
var type = action.at("type").valueOf();
|
||||
var obj = Object.create(null);
|
||||
var supported = true;
|
||||
|
||||
obj.type = type;
|
||||
|
||||
switch (type) {
|
||||
case 0:
|
||||
var act = action.at("action").toString();
|
||||
var actObj = standardActions[act];
|
||||
if (actObj === undefined) {
|
||||
this.trigger("seviceMessage", "Action " + act + " is not supported in ImagePanel, skipping");
|
||||
supported = false;
|
||||
}
|
||||
var model = new LM(actionProps, "");
|
||||
model.enabled = true;
|
||||
model.hasLabel = true;
|
||||
model.label = new LM(actionLabelProps, actObj.name);
|
||||
model.addController(model.label);
|
||||
model.activate = this._actionActivate.bind(this, act);
|
||||
|
||||
obj.model = model;
|
||||
obj.action = act;
|
||||
break;
|
||||
}
|
||||
|
||||
if (supported) {
|
||||
this._actions.push(obj);
|
||||
this.trigger("addAction", obj.model);
|
||||
}
|
||||
},
|
||||
"addElement": function(key, element) {
|
||||
switch (key) {
|
||||
case "image":
|
||||
if (!this._hasImage) {
|
||||
this._hasImage = true;
|
||||
this.image = new File(new Address(["images", element.toString()]));
|
||||
this.addForeignController("Corax", this.image);
|
||||
}
|
||||
break;
|
||||
case "hasPageLink":
|
||||
this._hasPageLink = element.valueOf();
|
||||
break;
|
||||
case "actions":
|
||||
var size = element.length();
|
||||
for (var i = 0; i < size; ++i) {
|
||||
this.addAction(element.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
Vocabulary.fn.addElement.call(this, key, element);
|
||||
},
|
||||
"getActions": function() {
|
||||
var models = [];
|
||||
for (var i = 0; i < this._actions.length; ++i) {
|
||||
models.push(this._actions[i].model);
|
||||
}
|
||||
|
||||
return models;
|
||||
},
|
||||
"getPageLink": function() {
|
||||
if (this._hasPageLink) {
|
||||
return this.data.at("pageLink").clone();
|
||||
} else {
|
||||
throw new Error("An attempt to request a page link from pane which doesn't have it");
|
||||
}
|
||||
},
|
||||
"hasImage": function() {
|
||||
return this._hasImage;
|
||||
},
|
||||
"hasPageLink": function() {
|
||||
return this._hasPageLink;
|
||||
},
|
||||
"removeElement": function(key) {
|
||||
Vocabulary.fn.removeElement.call(this, key);
|
||||
|
||||
@ -37,4 +108,32 @@ var ImagePane = Vocabulary.inherit({
|
||||
}
|
||||
});
|
||||
|
||||
var standardActions = {
|
||||
"play": {
|
||||
handler: function (obj) {
|
||||
var id = obj._pairAddress.back(); //todo it's a kind of crutch, need to do something about it in the future
|
||||
window.play(id);
|
||||
id.destructor();
|
||||
},
|
||||
name: "Play"
|
||||
},
|
||||
"scheduledToPlay": {
|
||||
handler: function(obj) {
|
||||
var id = obj._pairAddress.back(); //todo it's a kind of crutch, need to do something about it in the future
|
||||
window.scheduleToPlay(id);
|
||||
id.destructor();
|
||||
},
|
||||
name: "Schedule"
|
||||
}
|
||||
};
|
||||
|
||||
var actionProps = {
|
||||
"backgroundColor": "primaryColor"
|
||||
};
|
||||
var actionLabelProps = {
|
||||
"fontFamily": "casualFont",
|
||||
"fontSize": "casualFontSize",
|
||||
"color": "primaryFontColor"
|
||||
}
|
||||
|
||||
module.exports = ImagePane;
|
||||
|
@ -10,16 +10,12 @@ var Link = Controller.inherit({
|
||||
"constructor": function(addr) {
|
||||
Controller.fn.constructor.call(this, addr);
|
||||
|
||||
var hop = new Address(["label"]);
|
||||
|
||||
this.targetAddress = new Address([]);
|
||||
this.label = new String(addr['+'](hop));
|
||||
|
||||
this.addController(this.label);
|
||||
|
||||
this.addHandler("get");
|
||||
|
||||
hop.destructor();
|
||||
},
|
||||
"destructor": function() {
|
||||
this.targetAddress.destructor();
|
||||
@ -35,4 +31,6 @@ var Link = Controller.inherit({
|
||||
}
|
||||
});
|
||||
|
||||
var hop = new Address(["label"]);
|
||||
|
||||
module.exports = Link;
|
||||
|
@ -1,14 +1,16 @@
|
||||
"use strict";
|
||||
var counter = 0;
|
||||
var Subscribable = require("../utils/subscribable");
|
||||
var Controller = require("./controller");
|
||||
|
||||
var LocalModel = Subscribable.inherit({
|
||||
"className": "LocalModel",
|
||||
"constructor": function(properties) {
|
||||
"constructor": function(properties, data) {
|
||||
Subscribable.fn.constructor.call(this);
|
||||
|
||||
this.properties = [];
|
||||
this._controllers = [];
|
||||
this._foreignControllers = [];
|
||||
|
||||
if (properties) {
|
||||
for (var key in properties) {
|
||||
@ -18,9 +20,67 @@ var LocalModel = Subscribable.inherit({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data !== undefined) {
|
||||
this.data = data;
|
||||
this.initialized = true;
|
||||
}
|
||||
},
|
||||
"destructor": function() {
|
||||
for (i = 0; i < this._foreignControllers.length; ++i) {
|
||||
var pair = this._foreignControllers[i];
|
||||
global.unsubscribeForeignController(pair.n, pair.c);
|
||||
global.unregisterForeignController(pair.n, pair.c);
|
||||
|
||||
pair.c.destructor();
|
||||
}
|
||||
|
||||
for (var i = 0; i < this._controllers.length; ++i) {
|
||||
this._controllers[i].destructor();
|
||||
}
|
||||
|
||||
Subscribable.fn.destructor.call(this);
|
||||
},
|
||||
"addController": function(ctrl) {
|
||||
this._controllers.push(ctrl);
|
||||
},
|
||||
"addForeignController": function(nodeName, ctrl) {
|
||||
if (!(ctrl instanceof Controller)) {
|
||||
throw new Error("An attempt to add not a controller into " + this.className);
|
||||
}
|
||||
|
||||
this._foreignControllers.push({n: nodeName, c: ctrl});
|
||||
ctrl.on("serviceMessage", this._onControllerServiceMessage, this);
|
||||
|
||||
global.registerForeignController(nodeName, ctrl);
|
||||
global.subscribeForeignController(nodeName, ctrl);
|
||||
},
|
||||
"_onControllerServiceMessage": Controller.fn._onControllerServiceMessage,
|
||||
"removeForeignController": function(ctrl) {
|
||||
if (!(ctrl instanceof Controller)) {
|
||||
throw new Error("An attempt to remove not a controller from " + this.className);
|
||||
}
|
||||
for (var i = 0; i < this._foreignControllers.length; ++i) {
|
||||
if (this._foreignControllers[i].c === ctrl) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i === this._foreignControllers.length) {
|
||||
throw new Error("An attempt to remove not not existing controller from " + this.className);
|
||||
} else {
|
||||
var pair = this._foreignControllers[i];
|
||||
global.unsubscribeForeignController(pair.n, pair.c);
|
||||
global.registerForeignController(pair.n, pair.c);
|
||||
|
||||
pair.c.off("serviceMessage", this._onControllerServiceMessage, this);
|
||||
|
||||
this._foreignControllers.splice(i, 1);
|
||||
}
|
||||
},
|
||||
"setData": function(data) {
|
||||
this.data = data;
|
||||
this.initialized = true;
|
||||
this.trigger("data");
|
||||
}
|
||||
});
|
||||
|
@ -9,15 +9,28 @@ var PanesList = List.inherit({
|
||||
"constructor": function PanesListModel(addr) {
|
||||
List.fn.constructor.call(this, addr);
|
||||
|
||||
this.actions = null;
|
||||
|
||||
this._subscriptionStart = 0;
|
||||
this._subscriptionEnd = Infinity;
|
||||
},
|
||||
"destructor": function() {
|
||||
if (this.initialized) {
|
||||
this.actions.destructor();
|
||||
}
|
||||
|
||||
List.fn.destructor.call(this);
|
||||
},
|
||||
"addElement": function(element) {
|
||||
var size = this.data.length();
|
||||
List.fn.addElement.call(this, element);
|
||||
|
||||
if (size >= this._subscriptionStart && size < this._subscriptionEnd) {
|
||||
var controller = new ImagePane(this._pairAddress["+"](new Address([element.toString()])));
|
||||
var size = this.actions.length();
|
||||
for (var i = 0; i < size; ++i) {
|
||||
controller.addAction(this.actions.at(i));
|
||||
}
|
||||
this.addController(controller);
|
||||
}
|
||||
},
|
||||
@ -25,6 +38,24 @@ var PanesList = List.inherit({
|
||||
List.fn.clear.call(this);
|
||||
this.clearChildren();
|
||||
},
|
||||
"_h_get": function(ev) {
|
||||
this.clear();
|
||||
|
||||
var root = ev.getData();
|
||||
var data = root.at("data");
|
||||
|
||||
if (this.initialized) {
|
||||
this.actions.destructor();
|
||||
}
|
||||
this.actions = root.at("actions").clone();
|
||||
|
||||
var size = data.length();
|
||||
for (var i = 0; i < size; ++i) {
|
||||
this.addElement(data.at(i).clone());
|
||||
}
|
||||
this.initialized = true;
|
||||
this.trigger("data");
|
||||
},
|
||||
"setSubscriptionRange": function(s, e) {
|
||||
var needStart = s !== this._subscriptionStart;
|
||||
var needEnd = e !== this._subscriptionEnd;
|
||||
@ -33,7 +64,8 @@ var PanesList = List.inherit({
|
||||
var oe = this._subscriptionEnd;
|
||||
this._subscriptionStart = s;
|
||||
this._subscriptionEnd = e;
|
||||
if (this._subscribed) {
|
||||
if (this._subscribed && this.initialized) {
|
||||
var size = this.actions.length();
|
||||
this.trigger("rangeStart");
|
||||
if (needStart) {
|
||||
if (s > os) {
|
||||
@ -47,6 +79,9 @@ var PanesList = List.inherit({
|
||||
var limit = Math.min(os, e) - s;
|
||||
for (var i = 0; i < limit; ++i) {
|
||||
var ctrl = new ImagePane(this._pairAddress["+"](new Address([this.data.at(i + s).toString()])));
|
||||
for (var a = 0; a < size; ++a) {
|
||||
ctrl.addAction(this.actions.at(a));
|
||||
}
|
||||
this.addController(ctrl, i);
|
||||
}
|
||||
}
|
||||
@ -59,6 +94,9 @@ var PanesList = List.inherit({
|
||||
var amount = ce - start; //it can be negative, it's fine
|
||||
for (var i = 0; i < amount; ++i) {
|
||||
var ctrl = new ImagePane(this._pairAddress["+"](new Address([this.data.at(start + i).toString()])));
|
||||
for (var a = 0; a < size; ++a) {
|
||||
ctrl.addAction(this.actions.at(a));
|
||||
}
|
||||
this.addController(ctrl);
|
||||
}
|
||||
} else if (ce < coe) {
|
||||
|
603
libjs/wController/player.js
Normal file
603
libjs/wController/player.js
Normal file
@ -0,0 +1,603 @@
|
||||
"use strict";
|
||||
|
||||
var Uint64 = require("../wType/uint64");
|
||||
var Address = require("../wType/address");
|
||||
|
||||
var Controller = require("./controller");
|
||||
var Button = require("./button");
|
||||
var ImageById = require("./imageById");
|
||||
var Vocabulary = require("./vocabulary");
|
||||
var Audio = require("./file/audio");
|
||||
var Model = require("./localModel");
|
||||
|
||||
var Enum = require("../utils/enum");
|
||||
var StateMachine = require("../utils/stateMachine");
|
||||
|
||||
var Player = Controller.inherit({
|
||||
className: "Player",
|
||||
constructor: function(addr) {
|
||||
Controller.fn.constructor.call(this, addr);
|
||||
|
||||
this.controls = Object.create(null);
|
||||
this.views = Object.create(null);
|
||||
this.mode = PlayerMode.straight.playback;
|
||||
this.progress = new ProgressModel();
|
||||
this.volume = new Slider();
|
||||
this.volume.setValue(1);
|
||||
this.progress.on("seekingStart", this._onSeekingStart, this);
|
||||
this.progress.on("seekingEnd", this._onSeekingEnd, this);
|
||||
this.progress.enable(false);
|
||||
this.volume.on("value", this._onVolume, this);
|
||||
this._audio = null;
|
||||
this._createStateMachine();
|
||||
this._createPlayingInfrastructure();
|
||||
|
||||
this.addHandler("get");
|
||||
this.addHandler("viewsChange");
|
||||
this.addHandler("play");
|
||||
this.addHandler("pause");
|
||||
|
||||
this._playbackInterval = setInterval(this._onInterval.bind(this), intervalPrecision);
|
||||
},
|
||||
destructor: function() {
|
||||
this._clearInterval(this._playbackInterval);
|
||||
this._destroyPlayingInfrastructure();
|
||||
this._fsm.destructor();
|
||||
this.progress.destructor();
|
||||
|
||||
Controller.fn.destructor.call(this);
|
||||
},
|
||||
_addControl: function(type, address) {
|
||||
var t = type.valueOf();
|
||||
|
||||
if (this.controls[t] !== undefined) {
|
||||
throw new Error("An attempt to add multiple instances of " + ItemType.reversed[t] + " into Player");
|
||||
}
|
||||
|
||||
if (ItemType.reversed[t] !== undefined) {
|
||||
switch (t) {
|
||||
case ItemType.straight.playPause:
|
||||
case ItemType.straight.prev:
|
||||
case ItemType.straight.next:
|
||||
var btn = new Button(address.clone());
|
||||
btn.itemType = t;
|
||||
this.controls[t] = btn;
|
||||
this.addController(btn);
|
||||
this.trigger("newElement", btn, t);
|
||||
break;
|
||||
default:
|
||||
this.trigger("serviceMessage", "An attempt to add ItemType " + ItemType.reversed[t] + " to controls of the Player, but it's not qualified to be a control", 1);
|
||||
}
|
||||
} else {
|
||||
this.trigger("serviceMessage", "An unrecgnized item ItemType in Player: " + t, 1);
|
||||
}
|
||||
},
|
||||
_addView: function(type, address) {
|
||||
var t = type.valueOf();
|
||||
var ctrl;
|
||||
var supported = false;
|
||||
|
||||
if (this.views[t] !== undefined) {
|
||||
throw new Error("An attempt to add multiple instances of " + ItemType.reversed[t] + " into Player");
|
||||
}
|
||||
|
||||
if (ItemType.reversed[t] !== undefined) {
|
||||
switch (t) {
|
||||
case ItemType.straight.queue:
|
||||
this.trigger("serviceMessage", "Queue is not supported yet in Player", 1);
|
||||
break;
|
||||
case ItemType.straight.currentPlayback:
|
||||
ctrl = new Vocabulary(address.clone());
|
||||
ctrl.on("newElement", this._onNewPlaybackElement, this);
|
||||
ctrl.on("removeElement", this._onRemovePlaybackElement, this);
|
||||
supported = true;
|
||||
break;
|
||||
case ItemType.straight.picture:
|
||||
ctrl = new ImageById(null, address.back());
|
||||
ctrl.ItemType = t;
|
||||
this.views[t] = ctrl;
|
||||
|
||||
this.trigger("newElement", ctrl, t);
|
||||
supported = false; //just to avoid adding with addController, since ImageById is not a controller
|
||||
break;
|
||||
default:
|
||||
this.trigger("serviceMessage", "An attempt to add ItemType " + ItemType.reversed[t] + " to views of the Player, but it's not qualified to be a view", 1);
|
||||
}
|
||||
} else {
|
||||
this.trigger("serviceMessage", "An unrecognized item ItemType in Player: " + t, 1);
|
||||
}
|
||||
|
||||
if (supported) {
|
||||
ctrl.ItemType = t;
|
||||
this.views[t] = ctrl;
|
||||
this.addController(ctrl);
|
||||
|
||||
this.trigger("newElement", ctrl, t);
|
||||
}
|
||||
},
|
||||
_checkIfEnough: function() {
|
||||
var diff = this._currentTime - this._seekingTime - this._ctx.currentTime;
|
||||
if (diff > threshold) {
|
||||
this._fsm.manipulation("enough");
|
||||
} else {
|
||||
this._fsm.manipulation("notEnough");
|
||||
}
|
||||
},
|
||||
_createPlayingInfrastructure: function() {
|
||||
this._ctx = new AudioContext();
|
||||
this._decoder = new Mp3Decoder();
|
||||
this._gainNode = this._ctx.createGain();
|
||||
this._gainNode.connect(this._ctx.destination);
|
||||
this._currentTime = 0;
|
||||
this._seekingTime = 0;
|
||||
this._buffers = [];
|
||||
this._sources = [];
|
||||
|
||||
this._ctx.suspend();
|
||||
},
|
||||
_createStateMachine: function() {
|
||||
this._fsm = new StateMachine("initial", graphs[this.mode]);
|
||||
this._fsm.on("stateChanged", this._onStateChanged, this);
|
||||
},
|
||||
_destroyPlayingInfrastructure: function() {
|
||||
this._ctx.close();
|
||||
this._decoder.delete();
|
||||
},
|
||||
getCurrentPlaybackTime: function() {
|
||||
return this._ctx.currentTime + this._seekingTime;
|
||||
},
|
||||
_h_get: function(ev) {
|
||||
var data = ev.getData();
|
||||
|
||||
var controls = data.at("controls");
|
||||
var views = data.at("views");
|
||||
var mode = data.at("mode").valueOf();
|
||||
var size, i, vc;
|
||||
|
||||
size = controls.length();
|
||||
for (i = 0; i < size; ++i) {
|
||||
vc = controls.at(i);
|
||||
this._addControl(vc.at("type"), vc.at("address"));
|
||||
}
|
||||
|
||||
size = views.length();
|
||||
for (i = 0; i < size; ++i) {
|
||||
vc = views.at(i);
|
||||
this._addView(vc.at("type"), vc.at("address"));
|
||||
}
|
||||
|
||||
if (this.mode !== mode) {
|
||||
if (PlayerMode.reversed[mode] === undefined) {
|
||||
throw new Error("Unsupported mode of player: " + mode);
|
||||
}
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
this.initialized = true;
|
||||
this.trigger("data");
|
||||
},
|
||||
_h_pause: function(ev) {
|
||||
this._fsm.manipulation("pause");
|
||||
},
|
||||
_h_play: function(ev) {
|
||||
this._fsm.manipulation("play");
|
||||
},
|
||||
_h_viewsChange: function(ev) {
|
||||
var data = ev.getData();
|
||||
|
||||
var add = data.at("add");
|
||||
var remove = data.at("remove");
|
||||
var size, i, vc;
|
||||
|
||||
size = remove.length();
|
||||
for (i = 0; i < size; ++i) {
|
||||
this._removeView(remove.at(i).valueOf());
|
||||
}
|
||||
|
||||
size = add.length();
|
||||
for (i = 0; i < size; ++i) {
|
||||
vc = add.at(i);
|
||||
this._addView(vc.at("type"), vc.at("address"));
|
||||
}
|
||||
},
|
||||
_onAudioNewSlice: function(frames) {
|
||||
var arr = new Uint8Array(frames.valueOf());
|
||||
this._decoder.addFragment(arr);
|
||||
|
||||
while (this._decoder.hasMore()) {
|
||||
var sb = this._decoder.decode(9999);
|
||||
if (sb === undefined) {
|
||||
break;
|
||||
} else {
|
||||
this._buffers.push(sb);
|
||||
|
||||
var startTime = this._currentTime - this._seekingTime;
|
||||
if (startTime < this._ctx.currentTime) {
|
||||
var offset = startTime - this._ctx.currentTime + sb.duration;
|
||||
if (offset > 0) {
|
||||
var src = this._ctx.createBufferSource();
|
||||
src.buffer = sb;
|
||||
src.connect(this._gainNode);
|
||||
src.start(0, Math.abs(startTime - this._ctx.currentTime));
|
||||
this._sources.push(src);
|
||||
}
|
||||
} else {
|
||||
var src = this._ctx.createBufferSource();
|
||||
src.buffer = sb;
|
||||
src.connect(this._gainNode);
|
||||
src.start(startTime);
|
||||
this._sources.push(src);
|
||||
}
|
||||
|
||||
this._currentTime += sb.duration;
|
||||
}
|
||||
}
|
||||
|
||||
this.progress.setLoad(this._currentTime / this._audio.getDuration());
|
||||
|
||||
this._fsm.manipulation("newFrames");
|
||||
if (this._audio.hasMore()) {
|
||||
this._audio.requestSlice(audioPortion);
|
||||
} else {
|
||||
this._fsm.manipulation("noMoreFrames");
|
||||
}
|
||||
},
|
||||
_onControllerReady: function() {
|
||||
this._fsm.manipulation("controllerReady");
|
||||
},
|
||||
_onInterval: function() {
|
||||
if (this._audio && this._audio.initialized && seekingStates.indexOf(this._fsm.state()) === -1) {
|
||||
var duration = this._audio.getDuration();
|
||||
this.progress.setValue(this.getCurrentPlaybackTime() / duration);
|
||||
this._checkIfEnough();
|
||||
|
||||
if (this.progress.value >= 0.9999) {
|
||||
var next = this.controls[ItemType.straight.next];
|
||||
if (next && next.enabled) {
|
||||
next.activate();
|
||||
} else {
|
||||
this._fsm.manipulation("pause");
|
||||
this._onSeekingStart();
|
||||
this._onSeekingEnd(0);
|
||||
this.controls[ItemType.straight.playPause].activate();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_onNewPlaybackElement: function(key, element) {
|
||||
switch (key) {
|
||||
case "image":
|
||||
var address = new Address(["images", element.toString()]);
|
||||
this._addView(new Uint64(ItemType.straight.picture), address);
|
||||
address.destructor();
|
||||
break;
|
||||
case "audio":
|
||||
if (this.mode === PlayerMode.straight.playback) {
|
||||
this._audio = new Audio(new Address(["music", element.toString()]));
|
||||
this.addForeignController("Corax", this._audio);
|
||||
this._audio.on("slice", this._onAudioNewSlice, this);
|
||||
this._audio.on("ready", this._onControllerReady, this);
|
||||
this._fsm.manipulation("controller");
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
_onRemovePlaybackElement: function(key) {
|
||||
switch (key) {
|
||||
case "image":
|
||||
this._removeView(ItemType.straight.picture);
|
||||
break;
|
||||
case "audio":
|
||||
this.removeForeignController(this._audio);
|
||||
this._audio.destructor();
|
||||
this._audio = null;
|
||||
}
|
||||
},
|
||||
_onSeekingStart: function() {
|
||||
this._fsm.manipulation("startSeeking");
|
||||
},
|
||||
_onSeekingEnd: function(progress) {
|
||||
if (seekingStates.indexOf(this._fsm.state()) !== -1) {
|
||||
for (var i = 0; i < this._sources.length; ++i) {
|
||||
this._sources[i].stop();
|
||||
}
|
||||
this._sources = [];
|
||||
|
||||
var ct = this.getCurrentPlaybackTime();
|
||||
var duration = this._audio.getDuration();
|
||||
var targetTime = duration * progress;
|
||||
this._seekingTime += targetTime - ct;
|
||||
|
||||
var nc = 0;
|
||||
|
||||
for (var i = 0; i < this._buffers.length; ++i) {
|
||||
var buffer = this._buffers[i];
|
||||
var startTime = nc - targetTime;
|
||||
if (startTime < 0) {
|
||||
var offset = startTime + buffer.duration;
|
||||
if (offset > 0) {
|
||||
var src = this._ctx.createBufferSource();
|
||||
src.buffer = buffer;
|
||||
src.connect(this._gainNode);
|
||||
src.start(0, Math.abs(startTime));
|
||||
this._sources.push(src);
|
||||
}
|
||||
} else {
|
||||
var src = this._ctx.createBufferSource();
|
||||
src.buffer = buffer;
|
||||
src.connect(this._gainNode);
|
||||
src.start(this._ctx.currentTime + startTime);
|
||||
this._sources.push(src);
|
||||
}
|
||||
|
||||
nc += buffer.duration;
|
||||
}
|
||||
}
|
||||
this._fsm.manipulation("stopSeeking");
|
||||
},
|
||||
_onStateChanged: function(e) {
|
||||
switch (e.newState) {
|
||||
case "initial":
|
||||
if (e.manipulation === "noController") {
|
||||
this.progress.enable(false);
|
||||
this.removeForeignController(this._audio);
|
||||
this._audio.destructor();
|
||||
this._audio = null;
|
||||
this._destroyPlayingInfrastructure();
|
||||
this._createPlayingInfrastructure();
|
||||
}
|
||||
break;
|
||||
case "initialPlaying":
|
||||
if (e.manipulation === "noController") {
|
||||
this.progress.enable(false);
|
||||
this._ctx.suspend();
|
||||
|
||||
this.removeForeignController(this._audio);
|
||||
this._audio.destructor();
|
||||
this._audio = null;
|
||||
this._destroyPlayingInfrastructure();
|
||||
this._createPlayingInfrastructure();
|
||||
}
|
||||
break;
|
||||
case "controllerNotReady":
|
||||
break
|
||||
case "controllerNotReadyPlaying":
|
||||
break
|
||||
case "hasController":
|
||||
break;
|
||||
case "hasControllerPlaying":
|
||||
if (this._audio.hasMore()) {
|
||||
this._audio.requestSlice(audioPortion);
|
||||
} else {
|
||||
this._fsm.manipulation("noMoreFrames");
|
||||
}
|
||||
break;
|
||||
case "buffering":
|
||||
if (e.oldState === "hasController") {
|
||||
this.progress.enable(true);
|
||||
}
|
||||
break;
|
||||
case "bufferingPlaying":
|
||||
if (e.oldState === "playing") {
|
||||
this._ctx.suspend();
|
||||
} else if (e.oldState === "hasControllerPlaying") {
|
||||
this.progress.enable(true);
|
||||
}
|
||||
break;
|
||||
case "seeking":
|
||||
break;
|
||||
case "seekingPlaying":
|
||||
if (e.oldState === "playing") {
|
||||
this._ctx.suspend();
|
||||
}
|
||||
break;
|
||||
case "seekingAllLoaded":
|
||||
break;
|
||||
case "seekingPlayingAllLoaded":
|
||||
if (e.oldState === "playingAllLoaded") {
|
||||
this._ctx.suspend();
|
||||
}
|
||||
break;
|
||||
case "paused":
|
||||
switch (e.oldState) {
|
||||
case "playing":
|
||||
this._ctx.suspend();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "pausedAllLoaded":
|
||||
switch (e.oldState) {
|
||||
case "playingAllLoaded":
|
||||
this._ctx.suspend();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "playing":
|
||||
this._ctx.resume();
|
||||
break;
|
||||
case "playingAllLoaded":
|
||||
switch (e.oldState) {
|
||||
case "pausedAllLoaded":
|
||||
case "bufferingPlaying":
|
||||
case "seekingPlayingAllLoaded":
|
||||
this._ctx.resume();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
_onVolume: function(volume) {
|
||||
this._gainNode.gain.cancelScheduledValues(this._ctx.currentTime);
|
||||
this._gainNode.gain.exponentialRampToValueAtTime(volume, this._ctx.currentTime + 0.01);
|
||||
},
|
||||
_removeControl: function(type) {
|
||||
var ctrl = this.controls[type];
|
||||
if (ctrl !== undefined) {
|
||||
this.trigger("removeElement", type);
|
||||
this.removeController(ctrl);
|
||||
ctrl.destructor();
|
||||
}
|
||||
},
|
||||
_removeView: function(type) {
|
||||
var view = this.views[type];
|
||||
if (view !== undefined) {
|
||||
this.trigger("removeElement", type);
|
||||
|
||||
if (type !== ItemType.straight.picture) {
|
||||
this.removeController(view);
|
||||
}
|
||||
if (type === ItemType.straight.currentPlayback) {
|
||||
if (this.views[ItemType.straight.picture]) {
|
||||
this._removeView(ItemType.straight.picture);
|
||||
}
|
||||
this._fsm.manipulation("noController");
|
||||
}
|
||||
delete this.views[type];
|
||||
view.destructor();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var ItemType = new Enum("ItemType");
|
||||
ItemType.add("playPause");
|
||||
ItemType.add("currentPlayback");
|
||||
ItemType.add("queue");
|
||||
ItemType.add("picture");
|
||||
ItemType.add("prev");
|
||||
ItemType.add("next");
|
||||
|
||||
var PlayerMode = new Enum("PlayerMode");
|
||||
PlayerMode.add("playback");
|
||||
|
||||
Player.ItemType = ItemType;
|
||||
|
||||
var graphs = Object.create(null);
|
||||
graphs[PlayerMode.straight.playback] = {
|
||||
"initial": {
|
||||
controller: "controllerNotReady",
|
||||
play: "initialPlaying"
|
||||
},
|
||||
"initialPlaying": {
|
||||
pause: "initial",
|
||||
controller: "controllerNotReadyPlaying"
|
||||
},
|
||||
"controllerNotReady": {
|
||||
play: "controllerNotReadyPlaying",
|
||||
controllerReady: "hasController"
|
||||
},
|
||||
"controllerNotReadyPlaying": {
|
||||
pause: "controllerNotReady",
|
||||
controllerReady: "hasControllerPlaying"
|
||||
},
|
||||
"hasController": {
|
||||
newFrames: "bufferingPlaying",
|
||||
play: "hasControllerPlaying",
|
||||
noController: "initial"
|
||||
},
|
||||
"hasControllerPlaying": {
|
||||
newFrames: "bufferingPlaying",
|
||||
pause: "hasController",
|
||||
noController: "initialPlaying"
|
||||
},
|
||||
"buffering": {
|
||||
play: "bufferingPlaying",
|
||||
enough: "paused",
|
||||
noMoreFrames: "pausedAllLoaded",
|
||||
startSeeking: "seeking",
|
||||
noController: "initial"
|
||||
},
|
||||
"bufferingPlaying": {
|
||||
pause: "buffering",
|
||||
enough: "playing",
|
||||
noMoreFrames: "playingAllLoaded",
|
||||
startSeeking: "seekingPlaying",
|
||||
noController: "initialPlaying"
|
||||
},
|
||||
"seeking": {
|
||||
stopSeeking: "buffering",
|
||||
noMoreFrames: "seekingAllLoaded",
|
||||
noController: "initial"
|
||||
},
|
||||
"seekingPlaying": {
|
||||
stopSeeking: "bufferingPlaying",
|
||||
noMoreFrames: "playingAllLoaded",
|
||||
noController: "initialPlaying"
|
||||
},
|
||||
"seekingAllLoaded": {
|
||||
stopSeeking: "pausedAllLoaded",
|
||||
noController: "initial"
|
||||
},
|
||||
"seekingPlayingAllLoaded": {
|
||||
stopSeeking: "playingAllLoaded",
|
||||
noController: "initialPlaying"
|
||||
},
|
||||
"paused": {
|
||||
play: "playing",
|
||||
notEnough: "buffering",
|
||||
noController: "initial",
|
||||
noMoreFrames: "pausedAllLoaded",
|
||||
startSeeking: "seeking"
|
||||
},
|
||||
"pausedAllLoaded": {
|
||||
play: "playingAllLoaded",
|
||||
noController: "initial",
|
||||
startSeeking: "seekingAllLoaded"
|
||||
},
|
||||
"playing": {
|
||||
pause: "paused",
|
||||
notEnough: "bufferingPlaying",
|
||||
noMoreFrames: "playingAllLoaded",
|
||||
noController: "initialPlaying",
|
||||
startSeeking: "seekingPlaying"
|
||||
},
|
||||
"playingAllLoaded": {
|
||||
pause: "pausedAllLoaded",
|
||||
noController: "initialPlaying",
|
||||
startSeeking: "seekingPlayingAllLoaded"
|
||||
}
|
||||
}
|
||||
var seekingStates = ["seeking", "seekingPlaying", "seekingAllLoaded", "seekingPlayingAllLoaded"]
|
||||
|
||||
var audioPortion = 1024 * 50; //bytes to download for each portion
|
||||
var threshold = 2; //seconds to buffer before playing
|
||||
var intervalPrecision = 100; //millisecond of how often to check the playback
|
||||
|
||||
var Slider = Model.inherit({
|
||||
className: "Slider",
|
||||
constructor: function(properties) {
|
||||
Model.fn.constructor.call(this, properties);
|
||||
|
||||
this.enabled = true;
|
||||
this.value = 0;
|
||||
this.initialized = true;
|
||||
},
|
||||
enable: function(en) {
|
||||
if (en !== this.enabled) {
|
||||
this.enabled = en;
|
||||
this.trigger("enabled", en);
|
||||
}
|
||||
},
|
||||
setValue: function(p) {
|
||||
if (p !== this.value) {
|
||||
this.value = p;
|
||||
this.trigger("value", p);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var ProgressModel = Slider.inherit({
|
||||
className: "ProgressModel",
|
||||
constructor: function(properties) {
|
||||
Slider.fn.constructor.call(this, properties);
|
||||
|
||||
this.value = 0;
|
||||
},
|
||||
setLoad: function(l) {
|
||||
if (l !== this.load) {
|
||||
this.load = l;
|
||||
this.trigger("load", l);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Player;
|
@ -45,7 +45,7 @@ var Vocabulary = Controller.inherit({
|
||||
|
||||
var keys = insert.getKeys();
|
||||
for (var j = 0; j < keys.length; ++j) {
|
||||
key = keys[i];
|
||||
key = keys[j];
|
||||
this.addElement(key, insert.at(key).clone());
|
||||
}
|
||||
this.trigger("change", data.clone());
|
||||
|
@ -13,5 +13,6 @@ configure_file(themeStorage.js themeStorage.js)
|
||||
configure_file(vocabulary.js vocabulary.js)
|
||||
configure_file(attributes.js attributes.js)
|
||||
configure_file(image.js image.js)
|
||||
configure_file(button.js button.js)
|
||||
|
||||
add_subdirectory(proxy)
|
||||
|
90
libjs/wModel/button.js
Normal file
90
libjs/wModel/button.js
Normal file
@ -0,0 +1,90 @@
|
||||
"use strict";
|
||||
|
||||
var Model = require("./model");
|
||||
var ModelString = require("./string");
|
||||
|
||||
var Vocabulary = require("../wType/vocabulary");
|
||||
var Boolean = require("../wType/boolean");
|
||||
var Address = require("../wType/address");
|
||||
var String = require("../wType/string");
|
||||
|
||||
var Button = Model.inherit({
|
||||
"className": "Button",
|
||||
"constructor": function(address) {
|
||||
Model.fn.constructor.call(this, address);
|
||||
|
||||
this._enabled = true;
|
||||
this._hasImage = false;
|
||||
this._hasLabel =false;
|
||||
this._imageName = undefined;
|
||||
this._label = undefined;
|
||||
|
||||
this.addHandler("get");
|
||||
this.addHandler("activate");
|
||||
},
|
||||
"setImage": function(name) {
|
||||
if (this._hasImage) {
|
||||
if (this._imageName !== name) {
|
||||
this._image = name;
|
||||
var vc = new Vocabulary();
|
||||
vc.insert("image", new String(this._imageName));
|
||||
this.broadcast(vc, "changeImage");
|
||||
}
|
||||
} else {
|
||||
this._image = name;
|
||||
this._hasImage = true;
|
||||
var vc = new Vocabulary();
|
||||
vc.insert("image", new String(this._imageName));
|
||||
this.broadcast(vc, "setImage");
|
||||
}
|
||||
},
|
||||
"setEnabled": function(enabled) {
|
||||
if (enabled !== this._enabled) {
|
||||
this._enabled = enabled;
|
||||
var vc = new Vocabulary();
|
||||
vc.insert("enabled", new Boolean(this._enabled));
|
||||
this.broadcast(vc, "setEnabled");
|
||||
}
|
||||
},
|
||||
"setLabel": function(text) {
|
||||
if (this._hasLabel) {
|
||||
this._label.set(text);
|
||||
} else {
|
||||
this._label = new ModelString(this._address["+"](labelHop), text);
|
||||
this.addModel(this._label);
|
||||
var vc = new Vocabulary();
|
||||
vc.insert("hasLabel", new Boolean(true));
|
||||
vc.insert("label", this._label.getAddress());
|
||||
this.broadcast(vc, "setLabel");
|
||||
this._hasLabel = true;
|
||||
}
|
||||
},
|
||||
"_h_subscribe": function(ev) {
|
||||
Model.fn._h_subscribe.call(this, ev);
|
||||
|
||||
this._h_get(ev);
|
||||
},
|
||||
"_h_get": function(ev) {
|
||||
var vc = new Vocabulary();
|
||||
vc.insert("hasImage", new Boolean(this._hasImage));
|
||||
if (this._hasImage) {
|
||||
vc.insert("image", new String(this._imageName));
|
||||
}
|
||||
vc.insert("hasLabel", new Boolean(this._hasLabel));
|
||||
if (this._hasLabel) {
|
||||
vc.insert("label", this._label.getAddress());
|
||||
}
|
||||
vc.insert("enabled", new Boolean(this._enabled));
|
||||
|
||||
this.response(vc, "get", ev);
|
||||
},
|
||||
"_h_activate": function() {
|
||||
if (this._enabled) {
|
||||
this.trigger("activated");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var labelHop = new Address(["label"]);
|
||||
|
||||
module.exports = Button;
|
@ -299,8 +299,10 @@ Model.ModelType = {
|
||||
String: 0,
|
||||
List: 1,
|
||||
Vocabulary: 2,
|
||||
Image: 3,
|
||||
Model: 4,
|
||||
//Catalogue: 3,
|
||||
Image: 4,
|
||||
Button: 5,
|
||||
Model: 6,
|
||||
|
||||
Attributes: 50,
|
||||
|
||||
@ -310,7 +312,8 @@ Model.ModelType = {
|
||||
PageStorage: 103,
|
||||
PanesList: 104,
|
||||
Theme: 105,
|
||||
ThemeStorage: 106
|
||||
ThemeStorage: 106,
|
||||
Player: 107
|
||||
};
|
||||
|
||||
module.exports = Model;
|
||||
|
@ -17,6 +17,36 @@ var Blob = Object.inherit({
|
||||
|
||||
return this.size() == other.size(); //TODO let's pretend one shall never wish to compare blobs)
|
||||
},
|
||||
"+=": function(other) {
|
||||
if (this.getType() !== other.getType()) {
|
||||
throw new Error("An attempt to add and assign an " + other.className + " to " + this.className);
|
||||
}
|
||||
|
||||
var newData = new ArrayBuffer(this._data.byteLength + other._data.byteLength);
|
||||
var newView = new Uint8Array(newData);
|
||||
var thisView = new Uint8Array(this._data);
|
||||
var otherView = new Uint8Array(other._data);
|
||||
|
||||
newView.set(thisView, 0);
|
||||
newView.set(otherView, this._data.byteLength);
|
||||
|
||||
this._data = newData;
|
||||
},
|
||||
"+": function(other) {
|
||||
if (this.getType() !== other.getType()) {
|
||||
throw new Error("An attempt to add an " + other.className + " to " + this.className);
|
||||
}
|
||||
|
||||
var newData = new ArrayBuffer(this._data.byteLength + other._data.byteLength);
|
||||
var newView = new Uint8Array(newData);
|
||||
var thisView = new Uint8Array(this._data);
|
||||
var otherView = new Uint8Array(other._data);
|
||||
|
||||
newView.set(thisView, 0);
|
||||
newView.set(otherView, this._data.byteLength);
|
||||
|
||||
return new Blob(newData);
|
||||
},
|
||||
"base64": function() {
|
||||
var arr = new Uint8Array(this._data);
|
||||
var bin = "";
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
var defineArray = [];
|
||||
defineArray.push("lib/utils/class");
|
||||
defineArray.push("lib/utils/enum");
|
||||
defineArray.push("lib/wSocket/socket");
|
||||
defineArray.push("lib/wDispatcher/dispatcher");
|
||||
defineArray.push("lib/wDispatcher/handler");
|
||||
@ -18,6 +19,7 @@
|
||||
defineArray.push("lib/wController/pageStorage");
|
||||
defineArray.push("lib/wController/page");
|
||||
defineArray.push("lib/wController/localModel");
|
||||
defineArray.push("lib/wController/player");
|
||||
|
||||
defineArray.push("views/view");
|
||||
defineArray.push("views/layout");
|
||||
@ -27,6 +29,7 @@
|
||||
|
||||
define(moduleName, defineArray, function lorgar_module() {
|
||||
var Class = require("lib/utils/class");
|
||||
var Enum = require("lib/utils/enum");
|
||||
var Socket = require("lib/wSocket/socket");
|
||||
var Dispatcher = require("lib/wDispatcher/dispatcher");
|
||||
var Handler = require("lib/wDispatcher/handler");
|
||||
@ -41,6 +44,7 @@
|
||||
var PageStorage = require("lib/wController/pageStorage");
|
||||
var PageController = require("lib/wController/page");
|
||||
var LocalModel = require("lib/wController/localModel");
|
||||
var PlayerModel = require("lib/wController/player");
|
||||
|
||||
var View = require("views/view");
|
||||
var Layout = require("views/layout");
|
||||
@ -53,16 +57,18 @@
|
||||
"constructor": function() {
|
||||
Class.fn.constructor.call(this);
|
||||
|
||||
this._playerCtl = undefined;
|
||||
this._currentPageCtl = undefined;
|
||||
this._nodes = Object.create(null);
|
||||
|
||||
this._initDispatcher();
|
||||
this._initModels();
|
||||
this._initViews();
|
||||
|
||||
this._prepareNode("Magnus", "localhost", 8081);
|
||||
this._prepareNode("Corax", "localhost", 8080);
|
||||
|
||||
this._initModels();
|
||||
this._initViews();
|
||||
this._registerModels();
|
||||
|
||||
this.connectNode("Magnus");
|
||||
this.connectNode("Corax");
|
||||
@ -70,6 +76,8 @@
|
||||
},
|
||||
"destructor": function() {
|
||||
window.onpopstate = undefined;
|
||||
this._unregisterModels();
|
||||
|
||||
if (this._currentPageCtl) {
|
||||
this._currentPage.destructor();
|
||||
this._currentPageCtl.destructor();
|
||||
@ -77,16 +85,17 @@
|
||||
|
||||
this._gc.destructor();
|
||||
this._ps.destructor();
|
||||
this._mainColorHelper.destructor();
|
||||
this._emptyHelper.destructor();
|
||||
|
||||
this._body.destructor();
|
||||
|
||||
this.coraxSocket.close();
|
||||
this.dispatcher.unregisterHandler(this._playerResponseHandler);
|
||||
this.dispatcher.unregisterDefaultHandler(this._logger);
|
||||
|
||||
this._logger.destructor();
|
||||
this.dispatcher.destructor();
|
||||
this._playerResponseHandler.destructor();
|
||||
//this.magnusSocket.destructor();
|
||||
//this.coraxSocket.destructor();
|
||||
|
||||
@ -106,31 +115,37 @@
|
||||
}
|
||||
|
||||
node.socket.open(node.address, node.port);
|
||||
node.state.setData(SocketState.straight.connecting);
|
||||
},
|
||||
"_initCoraxSocket": function() {
|
||||
this.coraxSocket = new Socket("Lorgar");
|
||||
this.coraxSocket.on("connected", this._coraxSocketConnected, this);
|
||||
this.coraxSocket.on("disconnected", this._coraxSocketDisconnected, this);
|
||||
this.coraxSocket.on("error", this._coraxSocketError, this);
|
||||
this.coraxSocket.on("message", this.dispatcher.pass, this.dispatcher);
|
||||
_registerModels: function () {
|
||||
this._gc.register(this.dispatcher, this._nodes.Magnus.socket);
|
||||
this._ps.register(this.dispatcher, this._nodes.Magnus.socket);
|
||||
},
|
||||
_unregisterModels: function() {
|
||||
if (this._currentPageCtl) {
|
||||
this._currentPageCtl.unregister();
|
||||
}
|
||||
|
||||
this._gc.unregister();
|
||||
this._ps.unregister();
|
||||
},
|
||||
"_initDispatcher": function() {
|
||||
this.dispatcher = new Dispatcher();
|
||||
this._logger = new Logger();
|
||||
this._playerResponseHandler = new Handler(new Address(["getPlayer"]), this, this._responsePlayer);
|
||||
this.dispatcher.registerDefaultHandler(this._logger);
|
||||
this.dispatcher.registerHandler(this._playerResponseHandler);
|
||||
},
|
||||
"_initModels": function() {
|
||||
this._gc = new GlobalControls(new Address(["magnus", "gc"]));
|
||||
this._ps = new PageStorage(new Address(["magnus", "ps"]));
|
||||
|
||||
this._mainColorHelper = new LocalModel({backgroundColor: "mainColor"});
|
||||
this._emptyHelper = new LocalModel();
|
||||
|
||||
this._gc.on("serviceMessage", this._onServiceMessage, this);
|
||||
this._ps.on("serviceMessage", this._onServiceMessage, this);
|
||||
|
||||
this._gc.on("themeSelected", this.setTheme, this);
|
||||
|
||||
this._gc.register(this.dispatcher, this._nodes.Magnus.socket);
|
||||
this._ps.register(this.dispatcher, this._nodes.Magnus.socket);
|
||||
|
||||
this._ps.on("pageName", this._onPageName, this);
|
||||
},
|
||||
"_initPageController": function(addr) {
|
||||
@ -150,18 +165,10 @@
|
||||
|
||||
document.body.innerHTML = "";
|
||||
document.body.appendChild(this._body._e);
|
||||
window.addEventListener("resize",this._onWindowResize.bind(this) ,false);
|
||||
window.addEventListener("resize", this._onWindowResize.bind(this), false);
|
||||
|
||||
this._body.setSize(document.body.offsetWidth, document.body.offsetHeight);
|
||||
this._body.append(this._mainLayout);
|
||||
var spacerL = new View(this._mainColorHelper, {
|
||||
maxWidth: 50
|
||||
});
|
||||
var spacerR = new View(this._mainColorHelper, {
|
||||
maxWidth: 50
|
||||
});
|
||||
this._mainLayout.append(spacerL, 1, 0, 1, 1);
|
||||
this._mainLayout.append(spacerR, 1, 2, 1, 1);
|
||||
},
|
||||
"_onHistoryPopState": function(e) {
|
||||
this._initPageController(new Address(e.state.address));
|
||||
@ -171,10 +178,29 @@
|
||||
address: this._currentPageCtl.getPairAddress().toArray()
|
||||
}, "", name);
|
||||
},
|
||||
"_onServiceMessage": function(text, severity) {
|
||||
var fn;
|
||||
|
||||
switch (severity) {
|
||||
case 2:
|
||||
fn = console.error;
|
||||
break;
|
||||
case 1:
|
||||
fn = console.warn;
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
fn = console.info;
|
||||
break;
|
||||
}
|
||||
|
||||
fn(text);
|
||||
},
|
||||
"_onSocketConnected": function(name) {
|
||||
console.log(name + " socket connected");
|
||||
var node = this._nodes[name];
|
||||
node.connected = true;
|
||||
node.state.setData(SocketState.straight.connected);
|
||||
|
||||
for (var id in node.foreigns) {
|
||||
if (node.foreigns[id].subscribed) {
|
||||
@ -182,13 +208,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (name === "Magnus") {
|
||||
this._gc.subscribe();
|
||||
switch (name) {
|
||||
case "Magnus":
|
||||
this._gc.subscribe();
|
||||
|
||||
if (!this._currentPageCtl) {
|
||||
this._ps.getPageAddress(location.pathname);
|
||||
this._ps.one("pageAddress", this._initPageController, this);
|
||||
}
|
||||
if (!this._currentPageCtl) {
|
||||
this._ps.getPageAddress(location.pathname);
|
||||
this._ps.one("pageAddress", this._initPageController, this);
|
||||
}
|
||||
break;
|
||||
case "Corax":
|
||||
this._requestPlayer();
|
||||
break;
|
||||
}
|
||||
},
|
||||
"_onSocketDisconnected": function(name) {
|
||||
@ -196,19 +227,44 @@
|
||||
var node = this._nodes[name];
|
||||
node.connected = false;
|
||||
|
||||
switch (name) {
|
||||
case "Corax":
|
||||
if (this._playerCtl) {
|
||||
this._mainLayout.removePlayer();
|
||||
this._playerCtl._onSocketDisconnected();
|
||||
this._playerCtl.unregister();
|
||||
this._playerCtl.destructor();
|
||||
this._playerCtl = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
for (var id in node.foreigns) {
|
||||
if (node.foreigns[id].subscribed) {
|
||||
node.foreigns[id].controller._onSocketDisconnected;
|
||||
node.foreigns[id].controller._onSocketDisconnected();
|
||||
}
|
||||
}
|
||||
node.state.setData(SocketState.straight.disconnected);
|
||||
},
|
||||
"_onSocketError": function(name) {
|
||||
"_onSocketError": function(name, e) {
|
||||
console.log(name + " socket error: ");
|
||||
console.log(e);
|
||||
},
|
||||
"_onWindowResize": function() {
|
||||
this._body.setSize(document.body.offsetWidth, document.body.offsetHeight);
|
||||
},
|
||||
"play": function(id) {
|
||||
if (this._nodes.Corax && this._nodes.Corax.connected) {
|
||||
var vc = new Vocabulary();
|
||||
vc.insert("id", id.clone());
|
||||
|
||||
var ev = new Event(this._playerCtl.getPairAddress()["+="](play), vc);
|
||||
|
||||
var socket = this._nodes.Corax.socket;
|
||||
ev.setSenderId(socket.getId().clone());
|
||||
socket.send(ev);
|
||||
ev.destructor();
|
||||
}
|
||||
},
|
||||
"_prepareNode": function(name, address, port) {
|
||||
if (this._nodes[name]) {
|
||||
throw new Error("An attempt to prepeare node " + name + " for the second time");
|
||||
@ -220,22 +276,27 @@
|
||||
obj.socket = new Socket("Lorgar");
|
||||
obj.connected = false;
|
||||
obj.foreigns = Object.create(null);
|
||||
obj.state = new LocalModel({fontFamily: "casualFont"});
|
||||
obj.state.enum = SocketState;
|
||||
obj.state.setData(SocketState.straight.disconnected);
|
||||
|
||||
obj.socket.on("connected", this._onSocketConnected.bind(this, name));
|
||||
obj.socket.on("disconnected", this._onSocketDisconnected.bind(this, name));
|
||||
obj.socket.on("error", this._onSocketError.bind(this, name));
|
||||
obj.socket.on("message", this.dispatcher.pass, this.dispatcher);
|
||||
|
||||
this._mainLayout.addState(name, obj.state);
|
||||
|
||||
this._nodes[name] = obj;
|
||||
},
|
||||
"registerForeignController": function(node, controller) {
|
||||
var node = this._nodes[node];
|
||||
"registerForeignController": function(nodeName, controller) {
|
||||
var node = this._nodes[nodeName];
|
||||
if (node === undefined) {
|
||||
throw new Error("An attempt to register controller to an unknown node " + node);
|
||||
throw new Error("An attempt to register controller to an unknown node " + nodeName);
|
||||
}
|
||||
|
||||
if (node.foreigns[controller.id] !== undefined) {
|
||||
throw new Error("An attempt to register a controller under node " + node + " for a second time");
|
||||
throw new Error("An attempt to register a controller under node " + nodeName + " for a second time");
|
||||
}
|
||||
var obj = Object.create(null);
|
||||
obj.controller = controller;
|
||||
@ -243,47 +304,89 @@
|
||||
node.foreigns[controller.id] = obj;
|
||||
controller.register(this.dispatcher, node.socket);
|
||||
},
|
||||
"_requestPlayer": function() {
|
||||
var vc = new Vocabulary();
|
||||
vc.insert("source", new Address([]));
|
||||
|
||||
var ev = new Event(new Address(["management", "givePlayer"]), vc);
|
||||
|
||||
var socket = this._nodes.Corax.socket;
|
||||
ev.setSenderId(socket.getId().clone());
|
||||
socket.send(ev);
|
||||
ev.destructor();
|
||||
},
|
||||
"_responsePlayer": function(ev) {
|
||||
var data = ev.getData();
|
||||
|
||||
this._playerCtl = new PlayerModel(data.at("address").clone());
|
||||
this._playerCtl.register(this.dispatcher, this._nodes["Corax"].socket);
|
||||
this._playerCtl.subscribe();
|
||||
this._mainLayout.appendPlayer(this._playerCtl);
|
||||
|
||||
this._playerCtl.on("serviceMessage", this._onServiceMessage, this);
|
||||
},
|
||||
"scheduleToPlay": function(id) {
|
||||
if (this._nodes.Corax && this._nodes.Corax.connected) {
|
||||
var vc = new Vocabulary();
|
||||
vc.insert("id", id.clone());
|
||||
|
||||
var ev = new Event(this._playerCtl.getPairAddress()["+="](queue), vc);
|
||||
|
||||
var socket = this._nodes.Corax.socket;
|
||||
ev.setSenderId(socket.getId().clone());
|
||||
socket.send(ev);
|
||||
ev.destructor();
|
||||
}
|
||||
},
|
||||
"setTheme": function(theme) {
|
||||
View.setTheme(theme);
|
||||
},
|
||||
"subscribeForeignController": function(node, controller) {
|
||||
var node = this._nodes[node];
|
||||
"subscribeForeignController": function(nodeName, controller) {
|
||||
var node = this._nodes[nodeName];
|
||||
if (node === undefined) {
|
||||
throw new Error("An attempt to subscribe a controller to an unknown node " + node);
|
||||
throw new Error("An attempt to subscribe a controller to an unknown node " + nodeName);
|
||||
}
|
||||
|
||||
if (node.foreigns[controller.id] === undefined) {
|
||||
throw new Error("An attempt to subscribe not registered controller to node " + node);
|
||||
throw new Error("An attempt to subscribe not registered controller to node " + nodeName);
|
||||
}
|
||||
node.foreigns[controller.id].subscribed = true;
|
||||
controller.subscribe();
|
||||
},
|
||||
"unregisterForeignController": function(node, controller) {
|
||||
var node = this._nodes[node];
|
||||
"unregisterForeignController": function(nodeName, controller) {
|
||||
var node = this._nodes[nodeName];
|
||||
if (node === undefined) {
|
||||
throw new Error("An attempt to unregister a controller from an unknown node " + node);
|
||||
throw new Error("An attempt to unregister a controller from an unknown node " + nodeName);
|
||||
}
|
||||
|
||||
if (node.foreigns[controller.id] === undefined) {
|
||||
throw new Error("An attempt to unregister not registered controller from node " + node);
|
||||
throw new Error("An attempt to unregister not registered controller from node " + nodeName);
|
||||
}
|
||||
delete node.foreigns[controller.id];
|
||||
controller.unregister();
|
||||
},
|
||||
"unsubscribeForeignController": function(node, controller) {
|
||||
var node = this._nodes[node];
|
||||
"unsubscribeForeignController": function(nodeName, controller) {
|
||||
var node = this._nodes[nodeName];
|
||||
if (node === undefined) {
|
||||
throw new Error("An attempt to unsubscribe a controller from an unknown node " + node);
|
||||
throw new Error("An attempt to unsubscribe a controller from an unknown node " + nodeName);
|
||||
}
|
||||
|
||||
if (node.foreigns[controller.id] === undefined) {
|
||||
throw new Error("An attempt to unsubscribe not registered controller from node " + node);
|
||||
throw new Error("An attempt to unsubscribe not registered controller from node " + nodeName);
|
||||
}
|
||||
node.foreigns[controller.id].subscribed = false;
|
||||
controller.unsubscribe();
|
||||
}
|
||||
});
|
||||
|
||||
var SocketState = new Enum("SocketState", ["description"]);
|
||||
SocketState.add("disconnected", {description: "Socket is disconnected"});
|
||||
SocketState.add("connecting", {description: "Socket is connecting to remote host"});
|
||||
SocketState.add("connected", {description: "Socket is connected"});
|
||||
|
||||
var queue = new Address(["queue"]);
|
||||
var play = new Address(["play"]);
|
||||
|
||||
return Lorgar;
|
||||
});
|
||||
})();
|
||||
|
@ -45,3 +45,15 @@ div.dragging .draggable {
|
||||
cursor: -moz-grabbing;
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.button {
|
||||
cursor: pointer
|
||||
}
|
||||
|
||||
.button.disabled {
|
||||
cursor: not-allowed
|
||||
}
|
||||
|
@ -10,3 +10,4 @@ add_subdirectory(wDispatcher)
|
||||
add_subdirectory(wTest)
|
||||
add_subdirectory(wController)
|
||||
add_subdirectory(fonts)
|
||||
add_subdirectory(em)
|
||||
|
12
lorgar/lib/em/CMakeLists.txt
Normal file
12
lorgar/lib/em/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
execute_process(COMMAND
|
||||
emcc --bind
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libmad.bc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/decoder.cpp
|
||||
-o ${CMAKE_CURRENT_BINARY_DIR}/wrapper.js
|
||||
-s FILESYSTEM=0
|
||||
-s ENVIRONMENT=web
|
||||
-O3
|
||||
--llvm-lto 1
|
||||
)
|
347
lorgar/lib/em/decoder.cpp
Normal file
347
lorgar/lib/em/decoder.cpp
Normal file
@ -0,0 +1,347 @@
|
||||
#include "decoder.h"
|
||||
#include <iostream>
|
||||
|
||||
Decoder::Decoder():
|
||||
state(empty),
|
||||
sampleRate(0),
|
||||
channels(0),
|
||||
cachedLength(0),
|
||||
samplesPerFrame(0),
|
||||
glue(new uint8_t[GLUE_LENGTH]),
|
||||
cachedNext(NULL),
|
||||
cachedThis(NULL),
|
||||
cachedError(MAD_ERROR_NONE),
|
||||
cached(false),
|
||||
synth(new mad_synth()),
|
||||
stream(new mad_stream()),
|
||||
frame(new mad_frame()),
|
||||
context(0),
|
||||
pending()
|
||||
{
|
||||
for (int i = 0; i < GLUE_LENGTH; ++i) {
|
||||
glue[i] = 0;
|
||||
}
|
||||
|
||||
mad_frame_init(frame);
|
||||
mad_stream_init(stream);
|
||||
mad_synth_init(synth);
|
||||
|
||||
emscripten::val AudioContext = emscripten::val::global("AudioContext");
|
||||
if (!AudioContext.as<bool>()) {
|
||||
AudioContext = emscripten::val::global("webkitAudioContext");
|
||||
}
|
||||
|
||||
context = AudioContext.new_();
|
||||
}
|
||||
|
||||
Decoder::~Decoder()
|
||||
{
|
||||
context.call<void>("close");
|
||||
|
||||
mad_synth_finish(synth);
|
||||
mad_stream_finish(stream);
|
||||
mad_frame_finish(frame);
|
||||
|
||||
delete synth;
|
||||
delete stream;
|
||||
delete frame;
|
||||
|
||||
delete[] glue;
|
||||
}
|
||||
|
||||
void Decoder::addFragment(const emscripten::val& array)
|
||||
{
|
||||
uint32_t length = array["length"].as<uint32_t>();
|
||||
|
||||
if (length < GLUE_LENGTH / 2) {
|
||||
std::cout << "Error: an attempt to add fragment smaller then half of the glue buffer, ignoring";
|
||||
return;
|
||||
}
|
||||
uint8_t* buffer = new uint8_t[length];
|
||||
|
||||
for (int i = 0; i < length; ++i) {
|
||||
buffer[i] = array[std::to_string(i)].as<uint8_t>();
|
||||
}
|
||||
|
||||
RawBuffer rb = {buffer, length};
|
||||
pending.push_back(rb);
|
||||
|
||||
switch (state) {
|
||||
case empty:
|
||||
mad_stream_buffer(stream, buffer, length);
|
||||
|
||||
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
|
||||
glue[i] = buffer[length - GLUE_LENGTH/2 + i];
|
||||
}
|
||||
|
||||
state = onBufferHalf;
|
||||
prepareNextBuffer();
|
||||
break;
|
||||
case onBufferHalf:
|
||||
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
|
||||
glue[GLUE_LENGTH/2 + i] = buffer[i];
|
||||
}
|
||||
|
||||
state = onBufferFull;
|
||||
break;
|
||||
case onBufferFull:
|
||||
break;
|
||||
case onGlueHalf:
|
||||
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
|
||||
glue[GLUE_LENGTH/2 + i] = buffer[i];
|
||||
}
|
||||
|
||||
state = onGlueFull;
|
||||
cached = false;
|
||||
prepareNextBuffer();
|
||||
break;
|
||||
case onGlueFull:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
emscripten::val Decoder::decode(uint32_t count)
|
||||
{
|
||||
emscripten::val ret = emscripten::val::undefined();
|
||||
|
||||
int available = framesLeft(count);
|
||||
int success = 0;
|
||||
if (available > 0) {
|
||||
ret = context.call<emscripten::val>("createBuffer", channels, available * samplesPerFrame, sampleRate);
|
||||
|
||||
std::vector<emscripten::val> chans(channels, emscripten::val::undefined());
|
||||
for (int i = 0; i < channels; ++i) {
|
||||
chans[i] = ret.call<emscripten::val>("getChannelData", i);
|
||||
}
|
||||
|
||||
for (int i = 0; success < available; ++i) {
|
||||
int res = mad_frame_decode(frame, stream);
|
||||
|
||||
if (res != 0) {
|
||||
if (MAD_RECOVERABLE(stream->error)) {
|
||||
|
||||
std::cout << "Unexpected error during the decoding process: " << mad_stream_errorstr(stream) << std::endl;
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mad_synth_frame(synth, frame);
|
||||
|
||||
for (int j = 0; j < samplesPerFrame; ++j) {
|
||||
for (int k = 0; k < channels; ++k) {
|
||||
float value = mad_f_todouble(synth->pcm.samples[k][j]);
|
||||
chans[k].set(std::to_string(success * samplesPerFrame + j), emscripten::val(value));
|
||||
}
|
||||
}
|
||||
++success;
|
||||
}
|
||||
|
||||
cachedLength -= available;
|
||||
#if DEBUGGING
|
||||
std::cout << "Processed " << available << " frames, " << success << " successfully, last error " << mad_stream_errorstr(stream) << std::endl;
|
||||
#endif
|
||||
if (cachedLength == 0) {
|
||||
cached = false;
|
||||
prepareNextBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Decoder::hasMore() const
|
||||
{
|
||||
if (pending.size() == 1) {
|
||||
return stream->error != MAD_ERROR_BUFLEN;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Decoder::framesLeft(uint32_t max)
|
||||
{
|
||||
if (state == empty || state == onGlueHalf) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cached == false) {
|
||||
mad_stream probe;
|
||||
mad_header ph;
|
||||
initializeProbe(probe);
|
||||
mad_header_init(&ph);
|
||||
sampleRate = 0;
|
||||
|
||||
while (cachedLength < max) {
|
||||
if (mad_header_decode(&ph, &probe) == 0) {
|
||||
if (sampleRate == 0) {
|
||||
sampleRate = ph.samplerate;
|
||||
channels = MAD_NCHANNELS(&ph);
|
||||
samplesPerFrame = MAD_NSBSAMPLES(&ph) * 32; //not sure why 32, it's in libmad source
|
||||
} else {
|
||||
if (sampleRate != ph.samplerate || channels != MAD_NCHANNELS(&ph) || samplesPerFrame != MAD_NSBSAMPLES(&ph) * 32) {
|
||||
if (cachedLength > 0) {
|
||||
#if DEBUGGING
|
||||
std::cout << "sample rate " << sampleRate << " -> " << ph.samplerate << std::endl;
|
||||
std::cout << "channels " << channels << " -> " << MAD_NCHANNELS(&ph) << std::endl;
|
||||
std::cout << "samples per frame " << samplesPerFrame << " -> " << MAD_NSBSAMPLES(&ph) * 32 << std::endl;
|
||||
#endif
|
||||
probe.next_frame = probe.this_frame;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (probe.next_frame > probe.this_frame) {
|
||||
++cachedLength;
|
||||
}
|
||||
} else {
|
||||
#if DEBUGGING
|
||||
std::cout << "framesLeft error: " << mad_stream_errorstr(&probe) << std::endl;
|
||||
#endif
|
||||
if (!MAD_RECOVERABLE(probe.error)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cachedNext = probe.next_frame;
|
||||
cachedThis = probe.this_frame;
|
||||
cachedError = probe.error;
|
||||
mad_header_finish(&ph);
|
||||
mad_stream_finish(&probe);
|
||||
#if DEBUGGING
|
||||
std::cout << cachedLength << " frames are available for decoding" << std::endl;
|
||||
#endif
|
||||
cached = true;
|
||||
}
|
||||
|
||||
return std::min(cachedLength, max);
|
||||
}
|
||||
|
||||
void Decoder::pullBuffer()
|
||||
{
|
||||
if (cached == false) {
|
||||
std::cout << "Error in pullBuffer method!" << std::endl;
|
||||
}
|
||||
stream->this_frame = cachedThis;
|
||||
stream->next_frame = cachedNext;
|
||||
stream->error = cachedError;
|
||||
}
|
||||
|
||||
void Decoder::changeBuffer()
|
||||
{
|
||||
switch (state) {
|
||||
case empty:
|
||||
std::cout << "Wrong state on switchBuffer method - empty, aborting" << std::endl;
|
||||
case onBufferHalf:
|
||||
switchToGlue();
|
||||
state = onGlueHalf;
|
||||
break;
|
||||
case onBufferFull:
|
||||
switchToGlue();
|
||||
state = onGlueFull;
|
||||
break;
|
||||
case onGlueHalf:
|
||||
std::cout << "Wrong state on switchBuffer method - onGlueHalf, aborting" << std::endl;
|
||||
break;
|
||||
case onGlueFull:
|
||||
#if DEBUGGING
|
||||
std::cout << "Having another fragment " << pending[0].length << " bytes long" << std::endl;
|
||||
#endif
|
||||
|
||||
switchBuffer(pending[0].ptr, pending[0].length);
|
||||
|
||||
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
|
||||
glue[i] = pending[0].ptr[pending[0].length - GLUE_LENGTH/2 + i];
|
||||
}
|
||||
|
||||
state = onBufferHalf;
|
||||
|
||||
if (pending.size() > 1) {
|
||||
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
|
||||
glue[GLUE_LENGTH/2 + i] = pending[1].ptr[i];
|
||||
}
|
||||
|
||||
state = onBufferFull;
|
||||
}
|
||||
}
|
||||
|
||||
cached = false;
|
||||
}
|
||||
|
||||
void Decoder::prepareNextBuffer()
|
||||
{
|
||||
bool shift;
|
||||
do {
|
||||
shift = false;
|
||||
framesLeft();
|
||||
if (cachedLength == 0 && state != empty && state != onGlueHalf) {
|
||||
pullBuffer();
|
||||
changeBuffer();
|
||||
shift = true;
|
||||
}
|
||||
} while (shift);
|
||||
}
|
||||
|
||||
void Decoder::initializeProbe(mad_stream& probe)
|
||||
{
|
||||
mad_stream_init(&probe);
|
||||
|
||||
probe.buffer = stream->buffer;
|
||||
probe.bufend = stream->bufend;
|
||||
probe.skiplen = stream->skiplen;
|
||||
//probe.sync = stream->sync;
|
||||
//probe.freerate = stream->freerate;
|
||||
//probe.this_frame = stream->this_frame;
|
||||
probe.next_frame = stream->next_frame;
|
||||
//probe.ptr.byte = stream->ptr.byte;
|
||||
//probe.ptr.cache = stream->ptr.cache;
|
||||
//probe.ptr.cache = stream->ptr.cache;
|
||||
//probe.anc_ptr.byte = stream->anc_ptr.byte;
|
||||
//probe.anc_ptr.cache = stream->anc_ptr.cache;
|
||||
//probe.anc_ptr.cache = stream->anc_ptr.cache;
|
||||
//probe.anc_bitlen = stream->anc_bitlen;
|
||||
//probe.main_data = stream.main_data;
|
||||
//probe.md_len = stream.md_len;
|
||||
//probe.options = stream->options;
|
||||
//probe.error = stream->error;
|
||||
}
|
||||
|
||||
void Decoder::switchToGlue()
|
||||
{
|
||||
#if DEBUGGING
|
||||
std::cout << "Switching to glue" << std::endl;
|
||||
#endif
|
||||
switchBuffer(glue, GLUE_LENGTH);
|
||||
|
||||
#if DEBUGGING
|
||||
std::cout << "Freeing the drained fragment" << std::endl;
|
||||
#endif
|
||||
delete[] pending[0].ptr;
|
||||
pending.pop_front();
|
||||
}
|
||||
|
||||
void Decoder::switchBuffer(uint8_t* bufferPtr, uint32_t length)
|
||||
{
|
||||
uint32_t left;
|
||||
|
||||
if (stream->error != MAD_ERROR_BUFLEN) {
|
||||
std::cout << "WARNING: Switching buffers while the previous one is not drained, last error: " << mad_stream_errorstr(stream) << std::endl;
|
||||
}
|
||||
if (stream->next_frame != NULL) {
|
||||
left = stream->bufend - stream->next_frame;
|
||||
} else {
|
||||
std::cout << "WARNING: not supposed to happen" << std::endl;
|
||||
}
|
||||
|
||||
if (left > GLUE_LENGTH / 2) {
|
||||
std::cout << "Error: bytes to read in the buffer are more then glue buffer can fit (" << left << ")" << std::endl;
|
||||
throw 1;
|
||||
}
|
||||
|
||||
mad_stream_buffer(stream, bufferPtr + GLUE_LENGTH / 2 - left, length - (GLUE_LENGTH / 2 - left));
|
||||
stream->error = MAD_ERROR_NONE;
|
||||
|
||||
while (mad_header_decode(&frame->header, stream) != 0 && stream->error != MAD_ERROR_BUFLEN) {}
|
||||
}
|
79
lorgar/lib/em/decoder.h
Normal file
79
lorgar/lib/em/decoder.h
Normal file
@ -0,0 +1,79 @@
|
||||
#ifndef DECODER_H
|
||||
#define DECODER_H
|
||||
|
||||
/**
|
||||
* @todo write docs
|
||||
*/
|
||||
#include <deque>
|
||||
|
||||
#include <emscripten/val.h>
|
||||
#include <emscripten/bind.h>
|
||||
#include "mad.h"
|
||||
|
||||
#define GLUE_LENGTH 6000
|
||||
|
||||
#define DEBUGGING false
|
||||
|
||||
class Decoder {
|
||||
public:
|
||||
Decoder();
|
||||
~Decoder();
|
||||
|
||||
void addFragment(const emscripten::val& array);
|
||||
emscripten::val decode(uint32_t count = UINT32_MAX);
|
||||
bool hasMore() const;
|
||||
uint32_t framesLeft(uint32_t max = UINT32_MAX);
|
||||
|
||||
private:
|
||||
|
||||
enum State {
|
||||
empty,
|
||||
onBufferHalf,
|
||||
onBufferFull,
|
||||
onGlueHalf,
|
||||
onGlueFull
|
||||
};
|
||||
|
||||
struct RawBuffer {
|
||||
uint8_t* ptr;
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
State state;
|
||||
|
||||
uint32_t sampleRate;
|
||||
uint8_t channels;
|
||||
uint32_t cachedLength;
|
||||
uint16_t samplesPerFrame;
|
||||
uint8_t* glue;
|
||||
uint8_t const* cachedNext;
|
||||
uint8_t const* cachedThis;
|
||||
mad_error cachedError;
|
||||
bool cached;
|
||||
|
||||
mad_synth* synth;
|
||||
mad_stream* stream;
|
||||
mad_frame* frame;
|
||||
emscripten::val context;
|
||||
|
||||
std::deque<RawBuffer> pending;
|
||||
|
||||
private:
|
||||
void pullBuffer();
|
||||
void changeBuffer();
|
||||
void prepareNextBuffer();
|
||||
void initializeProbe(mad_stream& probe);
|
||||
void switchToGlue();
|
||||
void switchBuffer(uint8_t* bufferPtr, uint32_t length);
|
||||
};
|
||||
|
||||
EMSCRIPTEN_BINDINGS(jsmad) {
|
||||
emscripten::class_<Decoder>("Decoder")
|
||||
.constructor<>()
|
||||
.function("addFragment", &Decoder::addFragment)
|
||||
.function("hasMore", &Decoder::hasMore)
|
||||
.function("framesLeft", &Decoder::framesLeft)
|
||||
.function("decode", &Decoder::decode);
|
||||
}
|
||||
|
||||
#endif // DECODER_H
|
BIN
lorgar/lib/em/libmad.bc
Normal file
BIN
lorgar/lib/em/libmad.bc
Normal file
Binary file not shown.
964
lorgar/lib/em/mad.h
Normal file
964
lorgar/lib/em/mad.h
Normal file
@ -0,0 +1,964 @@
|
||||
/*
|
||||
* libmad - MPEG audio decoder library
|
||||
* Copyright (C) 2000-2004 Underbit Technologies, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* If you would like to negotiate alternate licensing terms, you may do
|
||||
* so by contacting: Underbit Technologies, Inc. <info@underbit.com>
|
||||
*/
|
||||
|
||||
# ifdef __cplusplus
|
||||
extern "C" {
|
||||
# endif
|
||||
|
||||
# define FPM_64BIT
|
||||
|
||||
|
||||
|
||||
# define SIZEOF_INT 4
|
||||
# define SIZEOF_LONG 4
|
||||
# define SIZEOF_LONG_LONG 8
|
||||
|
||||
|
||||
/* Id: version.h,v 1.26 2004/01/23 09:41:33 rob Exp */
|
||||
|
||||
# ifndef LIBMAD_VERSION_H
|
||||
# define LIBMAD_VERSION_H
|
||||
|
||||
# define MAD_VERSION_MAJOR 0
|
||||
# define MAD_VERSION_MINOR 15
|
||||
# define MAD_VERSION_PATCH 1
|
||||
# define MAD_VERSION_EXTRA " (beta)"
|
||||
|
||||
# define MAD_VERSION_STRINGIZE(str) #str
|
||||
# define MAD_VERSION_STRING(num) MAD_VERSION_STRINGIZE(num)
|
||||
|
||||
# define MAD_VERSION MAD_VERSION_STRING(MAD_VERSION_MAJOR) "." \
|
||||
MAD_VERSION_STRING(MAD_VERSION_MINOR) "." \
|
||||
MAD_VERSION_STRING(MAD_VERSION_PATCH) \
|
||||
MAD_VERSION_EXTRA
|
||||
|
||||
# define MAD_PUBLISHYEAR "2000-2004"
|
||||
# define MAD_AUTHOR "Underbit Technologies, Inc."
|
||||
# define MAD_EMAIL "info@underbit.com"
|
||||
|
||||
extern char const mad_version[];
|
||||
extern char const mad_copyright[];
|
||||
extern char const mad_author[];
|
||||
extern char const mad_build[];
|
||||
|
||||
# endif
|
||||
|
||||
/* Id: fixed.h,v 1.38 2004/02/17 02:02:03 rob Exp */
|
||||
|
||||
# ifndef LIBMAD_FIXED_H
|
||||
# define LIBMAD_FIXED_H
|
||||
|
||||
# if SIZEOF_INT >= 4
|
||||
typedef signed int mad_fixed_t;
|
||||
|
||||
typedef signed int mad_fixed64hi_t;
|
||||
typedef unsigned int mad_fixed64lo_t;
|
||||
# else
|
||||
typedef signed long mad_fixed_t;
|
||||
|
||||
typedef signed long mad_fixed64hi_t;
|
||||
typedef unsigned long mad_fixed64lo_t;
|
||||
# endif
|
||||
|
||||
# if defined(_MSC_VER)
|
||||
# define mad_fixed64_t signed __int64
|
||||
# elif 1 || defined(__GNUC__)
|
||||
# define mad_fixed64_t signed long long
|
||||
# endif
|
||||
|
||||
# if defined(FPM_FLOAT)
|
||||
typedef double mad_sample_t;
|
||||
# else
|
||||
typedef mad_fixed_t mad_sample_t;
|
||||
# endif
|
||||
|
||||
/*
|
||||
* Fixed-point format: 0xABBBBBBB
|
||||
* A == whole part (sign + 3 bits)
|
||||
* B == fractional part (28 bits)
|
||||
*
|
||||
* Values are signed two's complement, so the effective range is:
|
||||
* 0x80000000 to 0x7fffffff
|
||||
* -8.0 to +7.9999999962747097015380859375
|
||||
*
|
||||
* The smallest representable value is:
|
||||
* 0x00000001 == 0.0000000037252902984619140625 (i.e. about 3.725e-9)
|
||||
*
|
||||
* 28 bits of fractional accuracy represent about
|
||||
* 8.6 digits of decimal accuracy.
|
||||
*
|
||||
* Fixed-point numbers can be added or subtracted as normal
|
||||
* integers, but multiplication requires shifting the 64-bit result
|
||||
* from 56 fractional bits back to 28 (and rounding.)
|
||||
*
|
||||
* Changing the definition of MAD_F_FRACBITS is only partially
|
||||
* supported, and must be done with care.
|
||||
*/
|
||||
|
||||
# define MAD_F_FRACBITS 28
|
||||
|
||||
# if MAD_F_FRACBITS == 28
|
||||
# define MAD_F(x) ((mad_fixed_t) (x##L))
|
||||
# else
|
||||
# if MAD_F_FRACBITS < 28
|
||||
# warning "MAD_F_FRACBITS < 28"
|
||||
# define MAD_F(x) ((mad_fixed_t) \
|
||||
(((x##L) + \
|
||||
(1L << (28 - MAD_F_FRACBITS - 1))) >> \
|
||||
(28 - MAD_F_FRACBITS)))
|
||||
# elif MAD_F_FRACBITS > 28
|
||||
# error "MAD_F_FRACBITS > 28 not currently supported"
|
||||
# define MAD_F(x) ((mad_fixed_t) \
|
||||
((x##L) << (MAD_F_FRACBITS - 28)))
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# define MAD_F_MIN ((mad_fixed_t) -0x80000000L)
|
||||
# define MAD_F_MAX ((mad_fixed_t) +0x7fffffffL)
|
||||
|
||||
# define MAD_F_ONE MAD_F(0x10000000)
|
||||
|
||||
# define mad_f_tofixed(x) ((mad_fixed_t) \
|
||||
((x) * (double) (1L << MAD_F_FRACBITS) + 0.5))
|
||||
# define mad_f_todouble(x) ((double) \
|
||||
((x) / (double) (1L << MAD_F_FRACBITS)))
|
||||
|
||||
# define mad_f_intpart(x) ((x) >> MAD_F_FRACBITS)
|
||||
# define mad_f_fracpart(x) ((x) & ((1L << MAD_F_FRACBITS) - 1))
|
||||
/* (x should be positive) */
|
||||
|
||||
# define mad_f_fromint(x) ((x) << MAD_F_FRACBITS)
|
||||
|
||||
# define mad_f_add(x, y) ((x) + (y))
|
||||
# define mad_f_sub(x, y) ((x) - (y))
|
||||
|
||||
# if defined(FPM_FLOAT)
|
||||
# error "FPM_FLOAT not yet supported"
|
||||
|
||||
# undef MAD_F
|
||||
# define MAD_F(x) mad_f_todouble(x)
|
||||
|
||||
# define mad_f_mul(x, y) ((x) * (y))
|
||||
# define mad_f_scale64
|
||||
|
||||
# undef ASO_ZEROCHECK
|
||||
|
||||
# elif defined(FPM_64BIT)
|
||||
|
||||
/*
|
||||
* This version should be the most accurate if 64-bit types are supported by
|
||||
* the compiler, although it may not be the most efficient.
|
||||
*/
|
||||
# if defined(OPT_ACCURACY)
|
||||
# define mad_f_mul(x, y) \
|
||||
((mad_fixed_t) \
|
||||
((((mad_fixed64_t) (x) * (y)) + \
|
||||
(1L << (MAD_F_SCALEBITS - 1))) >> MAD_F_SCALEBITS))
|
||||
# else
|
||||
# define mad_f_mul(x, y) \
|
||||
((mad_fixed_t) (((mad_fixed64_t) (x) * (y)) >> MAD_F_SCALEBITS))
|
||||
# endif
|
||||
|
||||
# define MAD_F_SCALEBITS MAD_F_FRACBITS
|
||||
|
||||
/* --- Intel --------------------------------------------------------------- */
|
||||
|
||||
# elif defined(FPM_INTEL)
|
||||
|
||||
# if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4035) /* no return value */
|
||||
static __forceinline
|
||||
mad_fixed_t mad_f_mul_inline(mad_fixed_t x, mad_fixed_t y)
|
||||
{
|
||||
enum {
|
||||
fracbits = MAD_F_FRACBITS
|
||||
};
|
||||
|
||||
__asm {
|
||||
mov eax, x
|
||||
imul y
|
||||
shrd eax, edx, fracbits
|
||||
}
|
||||
|
||||
/* implicit return of eax */
|
||||
}
|
||||
# pragma warning(pop)
|
||||
|
||||
# define mad_f_mul mad_f_mul_inline
|
||||
# define mad_f_scale64
|
||||
# else
|
||||
/*
|
||||
* This Intel version is fast and accurate; the disposition of the least
|
||||
* significant bit depends on OPT_ACCURACY via mad_f_scale64().
|
||||
*/
|
||||
# define MAD_F_MLX(hi, lo, x, y) \
|
||||
asm ("imull %3" \
|
||||
: "=a" (lo), "=d" (hi) \
|
||||
: "%a" (x), "rm" (y) \
|
||||
: "cc")
|
||||
|
||||
# if defined(OPT_ACCURACY)
|
||||
/*
|
||||
* This gives best accuracy but is not very fast.
|
||||
*/
|
||||
# define MAD_F_MLA(hi, lo, x, y) \
|
||||
({ mad_fixed64hi_t __hi; \
|
||||
mad_fixed64lo_t __lo; \
|
||||
MAD_F_MLX(__hi, __lo, (x), (y)); \
|
||||
asm ("addl %2,%0\n\t" \
|
||||
"adcl %3,%1" \
|
||||
: "=rm" (lo), "=rm" (hi) \
|
||||
: "r" (__lo), "r" (__hi), "0" (lo), "1" (hi) \
|
||||
: "cc"); \
|
||||
})
|
||||
# endif /* OPT_ACCURACY */
|
||||
|
||||
# if defined(OPT_ACCURACY)
|
||||
/*
|
||||
* Surprisingly, this is faster than SHRD followed by ADC.
|
||||
*/
|
||||
# define mad_f_scale64(hi, lo) \
|
||||
({ mad_fixed64hi_t __hi_; \
|
||||
mad_fixed64lo_t __lo_; \
|
||||
mad_fixed_t __result; \
|
||||
asm ("addl %4,%2\n\t" \
|
||||
"adcl %5,%3" \
|
||||
: "=rm" (__lo_), "=rm" (__hi_) \
|
||||
: "0" (lo), "1" (hi), \
|
||||
"ir" (1L << (MAD_F_SCALEBITS - 1)), "ir" (0) \
|
||||
: "cc"); \
|
||||
asm ("shrdl %3,%2,%1" \
|
||||
: "=rm" (__result) \
|
||||
: "0" (__lo_), "r" (__hi_), "I" (MAD_F_SCALEBITS) \
|
||||
: "cc"); \
|
||||
__result; \
|
||||
})
|
||||
# elif defined(OPT_INTEL)
|
||||
/*
|
||||
* Alternate Intel scaling that may or may not perform better.
|
||||
*/
|
||||
# define mad_f_scale64(hi, lo) \
|
||||
({ mad_fixed_t __result; \
|
||||
asm ("shrl %3,%1\n\t" \
|
||||
"shll %4,%2\n\t" \
|
||||
"orl %2,%1" \
|
||||
: "=rm" (__result) \
|
||||
: "0" (lo), "r" (hi), \
|
||||
"I" (MAD_F_SCALEBITS), "I" (32 - MAD_F_SCALEBITS) \
|
||||
: "cc"); \
|
||||
__result; \
|
||||
})
|
||||
# else
|
||||
# define mad_f_scale64(hi, lo) \
|
||||
({ mad_fixed_t __result; \
|
||||
asm ("shrdl %3,%2,%1" \
|
||||
: "=rm" (__result) \
|
||||
: "0" (lo), "r" (hi), "I" (MAD_F_SCALEBITS) \
|
||||
: "cc"); \
|
||||
__result; \
|
||||
})
|
||||
# endif /* OPT_ACCURACY */
|
||||
|
||||
# define MAD_F_SCALEBITS MAD_F_FRACBITS
|
||||
# endif
|
||||
|
||||
/* --- ARM ----------------------------------------------------------------- */
|
||||
|
||||
# elif defined(FPM_ARM)
|
||||
|
||||
/*
|
||||
* This ARM V4 version is as accurate as FPM_64BIT but much faster. The
|
||||
* least significant bit is properly rounded at no CPU cycle cost!
|
||||
*/
|
||||
# if 1
|
||||
/*
|
||||
* This is faster than the default implementation via MAD_F_MLX() and
|
||||
* mad_f_scale64().
|
||||
*/
|
||||
# define mad_f_mul(x, y) \
|
||||
({ mad_fixed64hi_t __hi; \
|
||||
mad_fixed64lo_t __lo; \
|
||||
mad_fixed_t __result; \
|
||||
asm ("smull %0, %1, %3, %4\n\t" \
|
||||
"movs %0, %0, lsr %5\n\t" \
|
||||
"adc %2, %0, %1, lsl %6" \
|
||||
: "=&r" (__lo), "=&r" (__hi), "=r" (__result) \
|
||||
: "%r" (x), "r" (y), \
|
||||
"M" (MAD_F_SCALEBITS), "M" (32 - MAD_F_SCALEBITS) \
|
||||
: "cc"); \
|
||||
__result; \
|
||||
})
|
||||
# endif
|
||||
|
||||
# define MAD_F_MLX(hi, lo, x, y) \
|
||||
asm ("smull %0, %1, %2, %3" \
|
||||
: "=&r" (lo), "=&r" (hi) \
|
||||
: "%r" (x), "r" (y))
|
||||
|
||||
# define MAD_F_MLA(hi, lo, x, y) \
|
||||
asm ("smlal %0, %1, %2, %3" \
|
||||
: "+r" (lo), "+r" (hi) \
|
||||
: "%r" (x), "r" (y))
|
||||
|
||||
# define MAD_F_MLN(hi, lo) \
|
||||
asm ("rsbs %0, %2, #0\n\t" \
|
||||
"rsc %1, %3, #0" \
|
||||
: "=r" (lo), "=r" (hi) \
|
||||
: "0" (lo), "1" (hi) \
|
||||
: "cc")
|
||||
|
||||
# define mad_f_scale64(hi, lo) \
|
||||
({ mad_fixed_t __result; \
|
||||
asm ("movs %0, %1, lsr %3\n\t" \
|
||||
"adc %0, %0, %2, lsl %4" \
|
||||
: "=&r" (__result) \
|
||||
: "r" (lo), "r" (hi), \
|
||||
"M" (MAD_F_SCALEBITS), "M" (32 - MAD_F_SCALEBITS) \
|
||||
: "cc"); \
|
||||
__result; \
|
||||
})
|
||||
|
||||
# define MAD_F_SCALEBITS MAD_F_FRACBITS
|
||||
|
||||
/* --- MIPS ---------------------------------------------------------------- */
|
||||
|
||||
# elif defined(FPM_MIPS)
|
||||
|
||||
/*
|
||||
* This MIPS version is fast and accurate; the disposition of the least
|
||||
* significant bit depends on OPT_ACCURACY via mad_f_scale64().
|
||||
*/
|
||||
# define MAD_F_MLX(hi, lo, x, y) \
|
||||
asm ("mult %2,%3" \
|
||||
: "=l" (lo), "=h" (hi) \
|
||||
: "%r" (x), "r" (y))
|
||||
|
||||
# if defined(HAVE_MADD_ASM)
|
||||
# define MAD_F_MLA(hi, lo, x, y) \
|
||||
asm ("madd %2,%3" \
|
||||
: "+l" (lo), "+h" (hi) \
|
||||
: "%r" (x), "r" (y))
|
||||
# elif defined(HAVE_MADD16_ASM)
|
||||
/*
|
||||
* This loses significant accuracy due to the 16-bit integer limit in the
|
||||
* multiply/accumulate instruction.
|
||||
*/
|
||||
# define MAD_F_ML0(hi, lo, x, y) \
|
||||
asm ("mult %2,%3" \
|
||||
: "=l" (lo), "=h" (hi) \
|
||||
: "%r" ((x) >> 12), "r" ((y) >> 16))
|
||||
# define MAD_F_MLA(hi, lo, x, y) \
|
||||
asm ("madd16 %2,%3" \
|
||||
: "+l" (lo), "+h" (hi) \
|
||||
: "%r" ((x) >> 12), "r" ((y) >> 16))
|
||||
# define MAD_F_MLZ(hi, lo) ((mad_fixed_t) (lo))
|
||||
# endif
|
||||
|
||||
# if defined(OPT_SPEED)
|
||||
# define mad_f_scale64(hi, lo) \
|
||||
((mad_fixed_t) ((hi) << (32 - MAD_F_SCALEBITS)))
|
||||
# define MAD_F_SCALEBITS MAD_F_FRACBITS
|
||||
# endif
|
||||
|
||||
/* --- SPARC --------------------------------------------------------------- */
|
||||
|
||||
# elif defined(FPM_SPARC)
|
||||
|
||||
/*
|
||||
* This SPARC V8 version is fast and accurate; the disposition of the least
|
||||
* significant bit depends on OPT_ACCURACY via mad_f_scale64().
|
||||
*/
|
||||
# define MAD_F_MLX(hi, lo, x, y) \
|
||||
asm ("smul %2, %3, %0\n\t" \
|
||||
"rd %%y, %1" \
|
||||
: "=r" (lo), "=r" (hi) \
|
||||
: "%r" (x), "rI" (y))
|
||||
|
||||
/* --- PowerPC ------------------------------------------------------------- */
|
||||
|
||||
# elif defined(FPM_PPC)
|
||||
|
||||
/*
|
||||
* This PowerPC version is fast and accurate; the disposition of the least
|
||||
* significant bit depends on OPT_ACCURACY via mad_f_scale64().
|
||||
*/
|
||||
# define MAD_F_MLX(hi, lo, x, y) \
|
||||
do { \
|
||||
asm ("mullw %0,%1,%2" \
|
||||
: "=r" (lo) \
|
||||
: "%r" (x), "r" (y)); \
|
||||
asm ("mulhw %0,%1,%2" \
|
||||
: "=r" (hi) \
|
||||
: "%r" (x), "r" (y)); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
# if defined(OPT_ACCURACY)
|
||||
/*
|
||||
* This gives best accuracy but is not very fast.
|
||||
*/
|
||||
# define MAD_F_MLA(hi, lo, x, y) \
|
||||
({ mad_fixed64hi_t __hi; \
|
||||
mad_fixed64lo_t __lo; \
|
||||
MAD_F_MLX(__hi, __lo, (x), (y)); \
|
||||
asm ("addc %0,%2,%3\n\t" \
|
||||
"adde %1,%4,%5" \
|
||||
: "=r" (lo), "=r" (hi) \
|
||||
: "%r" (lo), "r" (__lo), \
|
||||
"%r" (hi), "r" (__hi) \
|
||||
: "xer"); \
|
||||
})
|
||||
# endif
|
||||
|
||||
# if defined(OPT_ACCURACY)
|
||||
/*
|
||||
* This is slower than the truncating version below it.
|
||||
*/
|
||||
# define mad_f_scale64(hi, lo) \
|
||||
({ mad_fixed_t __result, __round; \
|
||||
asm ("rotrwi %0,%1,%2" \
|
||||
: "=r" (__result) \
|
||||
: "r" (lo), "i" (MAD_F_SCALEBITS)); \
|
||||
asm ("extrwi %0,%1,1,0" \
|
||||
: "=r" (__round) \
|
||||
: "r" (__result)); \
|
||||
asm ("insrwi %0,%1,%2,0" \
|
||||
: "+r" (__result) \
|
||||
: "r" (hi), "i" (MAD_F_SCALEBITS)); \
|
||||
asm ("add %0,%1,%2" \
|
||||
: "=r" (__result) \
|
||||
: "%r" (__result), "r" (__round)); \
|
||||
__result; \
|
||||
})
|
||||
# else
|
||||
# define mad_f_scale64(hi, lo) \
|
||||
({ mad_fixed_t __result; \
|
||||
asm ("rotrwi %0,%1,%2" \
|
||||
: "=r" (__result) \
|
||||
: "r" (lo), "i" (MAD_F_SCALEBITS)); \
|
||||
asm ("insrwi %0,%1,%2,0" \
|
||||
: "+r" (__result) \
|
||||
: "r" (hi), "i" (MAD_F_SCALEBITS)); \
|
||||
__result; \
|
||||
})
|
||||
# endif
|
||||
|
||||
# define MAD_F_SCALEBITS MAD_F_FRACBITS
|
||||
|
||||
/* --- Default ------------------------------------------------------------- */
|
||||
|
||||
# elif defined(FPM_DEFAULT)
|
||||
|
||||
/*
|
||||
* This version is the most portable but it loses significant accuracy.
|
||||
* Furthermore, accuracy is biased against the second argument, so care
|
||||
* should be taken when ordering operands.
|
||||
*
|
||||
* The scale factors are constant as this is not used with SSO.
|
||||
*
|
||||
* Pre-rounding is required to stay within the limits of compliance.
|
||||
*/
|
||||
# if defined(OPT_SPEED)
|
||||
# define mad_f_mul(x, y) (((x) >> 12) * ((y) >> 16))
|
||||
# else
|
||||
# define mad_f_mul(x, y) ((((x) + (1L << 11)) >> 12) * \
|
||||
(((y) + (1L << 15)) >> 16))
|
||||
# endif
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
# else
|
||||
# error "no FPM selected"
|
||||
# endif
|
||||
|
||||
/* default implementations */
|
||||
|
||||
# if !defined(mad_f_mul)
|
||||
# define mad_f_mul(x, y) \
|
||||
({ register mad_fixed64hi_t __hi; \
|
||||
register mad_fixed64lo_t __lo; \
|
||||
MAD_F_MLX(__hi, __lo, (x), (y)); \
|
||||
mad_f_scale64(__hi, __lo); \
|
||||
})
|
||||
# endif
|
||||
|
||||
# if !defined(MAD_F_MLA)
|
||||
# define MAD_F_ML0(hi, lo, x, y) ((lo) = mad_f_mul((x), (y)))
|
||||
# define MAD_F_MLA(hi, lo, x, y) ((lo) += mad_f_mul((x), (y)))
|
||||
# define MAD_F_MLN(hi, lo) ((lo) = -(lo))
|
||||
# define MAD_F_MLZ(hi, lo) ((void) (hi), (mad_fixed_t) (lo))
|
||||
# endif
|
||||
|
||||
# if !defined(MAD_F_ML0)
|
||||
# define MAD_F_ML0(hi, lo, x, y) MAD_F_MLX((hi), (lo), (x), (y))
|
||||
# endif
|
||||
|
||||
# if !defined(MAD_F_MLN)
|
||||
# define MAD_F_MLN(hi, lo) ((hi) = ((lo) = -(lo)) ? ~(hi) : -(hi))
|
||||
# endif
|
||||
|
||||
# if !defined(MAD_F_MLZ)
|
||||
# define MAD_F_MLZ(hi, lo) mad_f_scale64((hi), (lo))
|
||||
# endif
|
||||
|
||||
# if !defined(mad_f_scale64)
|
||||
# if defined(OPT_ACCURACY)
|
||||
# define mad_f_scale64(hi, lo) \
|
||||
((((mad_fixed_t) \
|
||||
(((hi) << (32 - (MAD_F_SCALEBITS - 1))) | \
|
||||
((lo) >> (MAD_F_SCALEBITS - 1)))) + 1) >> 1)
|
||||
# else
|
||||
# define mad_f_scale64(hi, lo) \
|
||||
((mad_fixed_t) \
|
||||
(((hi) << (32 - MAD_F_SCALEBITS)) | \
|
||||
((lo) >> MAD_F_SCALEBITS)))
|
||||
# endif
|
||||
# define MAD_F_SCALEBITS MAD_F_FRACBITS
|
||||
# endif
|
||||
|
||||
/* C routines */
|
||||
|
||||
mad_fixed_t mad_f_abs(mad_fixed_t);
|
||||
mad_fixed_t mad_f_div(mad_fixed_t, mad_fixed_t);
|
||||
|
||||
# endif
|
||||
|
||||
/* Id: bit.h,v 1.12 2004/01/23 09:41:32 rob Exp */
|
||||
|
||||
# ifndef LIBMAD_BIT_H
|
||||
# define LIBMAD_BIT_H
|
||||
|
||||
struct mad_bitptr {
|
||||
unsigned char const *byte;
|
||||
unsigned short cache;
|
||||
unsigned short left;
|
||||
};
|
||||
|
||||
void mad_bit_init(struct mad_bitptr *, unsigned char const *);
|
||||
|
||||
# define mad_bit_finish(bitptr) /* nothing */
|
||||
|
||||
unsigned int mad_bit_length(struct mad_bitptr const *,
|
||||
struct mad_bitptr const *);
|
||||
|
||||
# define mad_bit_bitsleft(bitptr) ((bitptr)->left)
|
||||
unsigned char const *mad_bit_nextbyte(struct mad_bitptr const *);
|
||||
|
||||
void mad_bit_skip(struct mad_bitptr *, unsigned int);
|
||||
unsigned long mad_bit_read(struct mad_bitptr *, unsigned int);
|
||||
void mad_bit_write(struct mad_bitptr *, unsigned int, unsigned long);
|
||||
|
||||
unsigned short mad_bit_crc(struct mad_bitptr, unsigned int, unsigned short);
|
||||
|
||||
# endif
|
||||
|
||||
/* Id: timer.h,v 1.16 2004/01/23 09:41:33 rob Exp */
|
||||
|
||||
# ifndef LIBMAD_TIMER_H
|
||||
# define LIBMAD_TIMER_H
|
||||
|
||||
typedef struct {
|
||||
signed long seconds; /* whole seconds */
|
||||
unsigned long fraction; /* 1/MAD_TIMER_RESOLUTION seconds */
|
||||
} mad_timer_t;
|
||||
|
||||
extern mad_timer_t const mad_timer_zero;
|
||||
|
||||
# define MAD_TIMER_RESOLUTION 352800000UL
|
||||
|
||||
enum mad_units {
|
||||
MAD_UNITS_HOURS = -2,
|
||||
MAD_UNITS_MINUTES = -1,
|
||||
MAD_UNITS_SECONDS = 0,
|
||||
|
||||
/* metric units */
|
||||
|
||||
MAD_UNITS_DECISECONDS = 10,
|
||||
MAD_UNITS_CENTISECONDS = 100,
|
||||
MAD_UNITS_MILLISECONDS = 1000,
|
||||
|
||||
/* audio sample units */
|
||||
|
||||
MAD_UNITS_8000_HZ = 8000,
|
||||
MAD_UNITS_11025_HZ = 11025,
|
||||
MAD_UNITS_12000_HZ = 12000,
|
||||
|
||||
MAD_UNITS_16000_HZ = 16000,
|
||||
MAD_UNITS_22050_HZ = 22050,
|
||||
MAD_UNITS_24000_HZ = 24000,
|
||||
|
||||
MAD_UNITS_32000_HZ = 32000,
|
||||
MAD_UNITS_44100_HZ = 44100,
|
||||
MAD_UNITS_48000_HZ = 48000,
|
||||
|
||||
/* video frame/field units */
|
||||
|
||||
MAD_UNITS_24_FPS = 24,
|
||||
MAD_UNITS_25_FPS = 25,
|
||||
MAD_UNITS_30_FPS = 30,
|
||||
MAD_UNITS_48_FPS = 48,
|
||||
MAD_UNITS_50_FPS = 50,
|
||||
MAD_UNITS_60_FPS = 60,
|
||||
|
||||
/* CD audio frames */
|
||||
|
||||
MAD_UNITS_75_FPS = 75,
|
||||
|
||||
/* video drop-frame units */
|
||||
|
||||
MAD_UNITS_23_976_FPS = -24,
|
||||
MAD_UNITS_24_975_FPS = -25,
|
||||
MAD_UNITS_29_97_FPS = -30,
|
||||
MAD_UNITS_47_952_FPS = -48,
|
||||
MAD_UNITS_49_95_FPS = -50,
|
||||
MAD_UNITS_59_94_FPS = -60
|
||||
};
|
||||
|
||||
# define mad_timer_reset(timer) ((void) (*(timer) = mad_timer_zero))
|
||||
|
||||
int mad_timer_compare(mad_timer_t, mad_timer_t);
|
||||
|
||||
# define mad_timer_sign(timer) mad_timer_compare((timer), mad_timer_zero)
|
||||
|
||||
void mad_timer_negate(mad_timer_t *);
|
||||
mad_timer_t mad_timer_abs(mad_timer_t);
|
||||
|
||||
void mad_timer_set(mad_timer_t *, unsigned long, unsigned long, unsigned long);
|
||||
void mad_timer_add(mad_timer_t *, mad_timer_t);
|
||||
void mad_timer_multiply(mad_timer_t *, signed long);
|
||||
|
||||
signed long mad_timer_count(mad_timer_t, enum mad_units);
|
||||
unsigned long mad_timer_fraction(mad_timer_t, unsigned long);
|
||||
void mad_timer_string(mad_timer_t, char *, char const *,
|
||||
enum mad_units, enum mad_units, unsigned long);
|
||||
|
||||
# endif
|
||||
|
||||
/* Id: stream.h,v 1.20 2004/02/05 09:02:39 rob Exp */
|
||||
|
||||
# ifndef LIBMAD_STREAM_H
|
||||
# define LIBMAD_STREAM_H
|
||||
|
||||
|
||||
# define MAD_BUFFER_GUARD 8
|
||||
# define MAD_BUFFER_MDLEN (511 + 2048 + MAD_BUFFER_GUARD)
|
||||
|
||||
enum mad_error {
|
||||
MAD_ERROR_NONE = 0x0000, /* no error */
|
||||
|
||||
MAD_ERROR_BUFLEN = 0x0001, /* input buffer too small (or EOF) */
|
||||
MAD_ERROR_BUFPTR = 0x0002, /* invalid (null) buffer pointer */
|
||||
|
||||
MAD_ERROR_NOMEM = 0x0031, /* not enough memory */
|
||||
|
||||
MAD_ERROR_LOSTSYNC = 0x0101, /* lost synchronization */
|
||||
MAD_ERROR_BADLAYER = 0x0102, /* reserved header layer value */
|
||||
MAD_ERROR_BADBITRATE = 0x0103, /* forbidden bitrate value */
|
||||
MAD_ERROR_BADSAMPLERATE = 0x0104, /* reserved sample frequency value */
|
||||
MAD_ERROR_BADEMPHASIS = 0x0105, /* reserved emphasis value */
|
||||
|
||||
MAD_ERROR_BADCRC = 0x0201, /* CRC check failed */
|
||||
MAD_ERROR_BADBITALLOC = 0x0211, /* forbidden bit allocation value */
|
||||
MAD_ERROR_BADSCALEFACTOR = 0x0221, /* bad scalefactor index */
|
||||
MAD_ERROR_BADMODE = 0x0222, /* bad bitrate/mode combination */
|
||||
MAD_ERROR_BADFRAMELEN = 0x0231, /* bad frame length */
|
||||
MAD_ERROR_BADBIGVALUES = 0x0232, /* bad big_values count */
|
||||
MAD_ERROR_BADBLOCKTYPE = 0x0233, /* reserved block_type */
|
||||
MAD_ERROR_BADSCFSI = 0x0234, /* bad scalefactor selection info */
|
||||
MAD_ERROR_BADDATAPTR = 0x0235, /* bad main_data_begin pointer */
|
||||
MAD_ERROR_BADPART3LEN = 0x0236, /* bad audio data length */
|
||||
MAD_ERROR_BADHUFFTABLE = 0x0237, /* bad Huffman table select */
|
||||
MAD_ERROR_BADHUFFDATA = 0x0238, /* Huffman data overrun */
|
||||
MAD_ERROR_BADSTEREO = 0x0239 /* incompatible block_type for JS */
|
||||
};
|
||||
|
||||
# define MAD_RECOVERABLE(error) ((error) & 0xff00)
|
||||
|
||||
struct mad_stream {
|
||||
unsigned char const *buffer; /* input bitstream buffer */
|
||||
unsigned char const *bufend; /* end of buffer */
|
||||
unsigned long skiplen; /* bytes to skip before next frame */
|
||||
|
||||
int sync; /* stream sync found */
|
||||
unsigned long freerate; /* free bitrate (fixed) */
|
||||
|
||||
unsigned char const *this_frame; /* start of current frame */
|
||||
unsigned char const *next_frame; /* start of next frame */
|
||||
struct mad_bitptr ptr; /* current processing bit pointer */
|
||||
|
||||
struct mad_bitptr anc_ptr; /* ancillary bits pointer */
|
||||
unsigned int anc_bitlen; /* number of ancillary bits */
|
||||
|
||||
unsigned char (*main_data)[MAD_BUFFER_MDLEN];
|
||||
/* Layer III main_data() */
|
||||
unsigned int md_len; /* bytes in main_data */
|
||||
|
||||
int options; /* decoding options (see below) */
|
||||
enum mad_error error; /* error code (see above) */
|
||||
};
|
||||
|
||||
enum {
|
||||
MAD_OPTION_IGNORECRC = 0x0001, /* ignore CRC errors */
|
||||
MAD_OPTION_HALFSAMPLERATE = 0x0002 /* generate PCM at 1/2 sample rate */
|
||||
# if 0 /* not yet implemented */
|
||||
MAD_OPTION_LEFTCHANNEL = 0x0010, /* decode left channel only */
|
||||
MAD_OPTION_RIGHTCHANNEL = 0x0020, /* decode right channel only */
|
||||
MAD_OPTION_SINGLECHANNEL = 0x0030 /* combine channels */
|
||||
# endif
|
||||
};
|
||||
|
||||
void mad_stream_init(struct mad_stream *);
|
||||
void mad_stream_finish(struct mad_stream *);
|
||||
|
||||
# define mad_stream_options(stream, opts) \
|
||||
((void) ((stream)->options = (opts)))
|
||||
|
||||
void mad_stream_buffer(struct mad_stream *,
|
||||
unsigned char const *, unsigned long);
|
||||
void mad_stream_skip(struct mad_stream *, unsigned long);
|
||||
|
||||
int mad_stream_sync(struct mad_stream *);
|
||||
|
||||
char const *mad_stream_errorstr(struct mad_stream const *);
|
||||
|
||||
# endif
|
||||
|
||||
/* Id: frame.h,v 1.20 2004/01/23 09:41:32 rob Exp */
|
||||
|
||||
# ifndef LIBMAD_FRAME_H
|
||||
# define LIBMAD_FRAME_H
|
||||
|
||||
|
||||
enum mad_layer {
|
||||
MAD_LAYER_I = 1, /* Layer I */
|
||||
MAD_LAYER_II = 2, /* Layer II */
|
||||
MAD_LAYER_III = 3 /* Layer III */
|
||||
};
|
||||
|
||||
enum mad_mode {
|
||||
MAD_MODE_SINGLE_CHANNEL = 0, /* single channel */
|
||||
MAD_MODE_DUAL_CHANNEL = 1, /* dual channel */
|
||||
MAD_MODE_JOINT_STEREO = 2, /* joint (MS/intensity) stereo */
|
||||
MAD_MODE_STEREO = 3 /* normal LR stereo */
|
||||
};
|
||||
|
||||
enum mad_emphasis {
|
||||
MAD_EMPHASIS_NONE = 0, /* no emphasis */
|
||||
MAD_EMPHASIS_50_15_US = 1, /* 50/15 microseconds emphasis */
|
||||
MAD_EMPHASIS_CCITT_J_17 = 3, /* CCITT J.17 emphasis */
|
||||
MAD_EMPHASIS_RESERVED = 2 /* unknown emphasis */
|
||||
};
|
||||
|
||||
struct mad_header {
|
||||
enum mad_layer layer; /* audio layer (1, 2, or 3) */
|
||||
enum mad_mode mode; /* channel mode (see above) */
|
||||
int mode_extension; /* additional mode info */
|
||||
enum mad_emphasis emphasis; /* de-emphasis to use (see above) */
|
||||
|
||||
unsigned long bitrate; /* stream bitrate (bps) */
|
||||
unsigned int samplerate; /* sampling frequency (Hz) */
|
||||
|
||||
unsigned short crc_check; /* frame CRC accumulator */
|
||||
unsigned short crc_target; /* final target CRC checksum */
|
||||
|
||||
int flags; /* flags (see below) */
|
||||
int private_bits; /* private bits (see below) */
|
||||
|
||||
mad_timer_t duration; /* audio playing time of frame */
|
||||
};
|
||||
|
||||
struct mad_frame {
|
||||
struct mad_header header; /* MPEG audio header */
|
||||
|
||||
int options; /* decoding options (from stream) */
|
||||
|
||||
mad_fixed_t sbsample[2][36][32]; /* synthesis subband filter samples */
|
||||
mad_fixed_t (*overlap)[2][32][18]; /* Layer III block overlap data */
|
||||
};
|
||||
|
||||
# define MAD_NCHANNELS(header) ((header)->mode ? 2 : 1)
|
||||
# define MAD_NSBSAMPLES(header) \
|
||||
((header)->layer == MAD_LAYER_I ? 12 : \
|
||||
(((header)->layer == MAD_LAYER_III && \
|
||||
((header)->flags & MAD_FLAG_LSF_EXT)) ? 18 : 36))
|
||||
|
||||
enum {
|
||||
MAD_FLAG_NPRIVATE_III = 0x0007, /* number of Layer III private bits */
|
||||
MAD_FLAG_INCOMPLETE = 0x0008, /* header but not data is decoded */
|
||||
|
||||
MAD_FLAG_PROTECTION = 0x0010, /* frame has CRC protection */
|
||||
MAD_FLAG_COPYRIGHT = 0x0020, /* frame is copyright */
|
||||
MAD_FLAG_ORIGINAL = 0x0040, /* frame is original (else copy) */
|
||||
MAD_FLAG_PADDING = 0x0080, /* frame has additional slot */
|
||||
|
||||
MAD_FLAG_I_STEREO = 0x0100, /* uses intensity joint stereo */
|
||||
MAD_FLAG_MS_STEREO = 0x0200, /* uses middle/side joint stereo */
|
||||
MAD_FLAG_FREEFORMAT = 0x0400, /* uses free format bitrate */
|
||||
|
||||
MAD_FLAG_LSF_EXT = 0x1000, /* lower sampling freq. extension */
|
||||
MAD_FLAG_MC_EXT = 0x2000, /* multichannel audio extension */
|
||||
MAD_FLAG_MPEG_2_5_EXT = 0x4000 /* MPEG 2.5 (unofficial) extension */
|
||||
};
|
||||
|
||||
enum {
|
||||
MAD_PRIVATE_HEADER = 0x0100, /* header private bit */
|
||||
MAD_PRIVATE_III = 0x001f /* Layer III private bits (up to 5) */
|
||||
};
|
||||
|
||||
void mad_header_init(struct mad_header *);
|
||||
|
||||
# define mad_header_finish(header) /* nothing */
|
||||
|
||||
int mad_header_decode(struct mad_header *, struct mad_stream *);
|
||||
|
||||
void mad_frame_init(struct mad_frame *);
|
||||
void mad_frame_finish(struct mad_frame *);
|
||||
|
||||
int mad_frame_decode(struct mad_frame *, struct mad_stream *);
|
||||
|
||||
void mad_frame_mute(struct mad_frame *);
|
||||
|
||||
# endif
|
||||
|
||||
/* Id: synth.h,v 1.15 2004/01/23 09:41:33 rob Exp */
|
||||
|
||||
# ifndef LIBMAD_SYNTH_H
|
||||
# define LIBMAD_SYNTH_H
|
||||
|
||||
|
||||
struct mad_pcm {
|
||||
unsigned int samplerate; /* sampling frequency (Hz) */
|
||||
unsigned short channels; /* number of channels */
|
||||
unsigned short length; /* number of samples per channel */
|
||||
mad_fixed_t samples[2][1152]; /* PCM output samples [ch][sample] */
|
||||
};
|
||||
|
||||
struct mad_synth {
|
||||
mad_fixed_t filter[2][2][2][16][8]; /* polyphase filterbank outputs */
|
||||
/* [ch][eo][peo][s][v] */
|
||||
|
||||
unsigned int phase; /* current processing phase */
|
||||
|
||||
struct mad_pcm pcm; /* PCM output */
|
||||
};
|
||||
|
||||
/* single channel PCM selector */
|
||||
enum {
|
||||
MAD_PCM_CHANNEL_SINGLE = 0
|
||||
};
|
||||
|
||||
/* dual channel PCM selector */
|
||||
enum {
|
||||
MAD_PCM_CHANNEL_DUAL_1 = 0,
|
||||
MAD_PCM_CHANNEL_DUAL_2 = 1
|
||||
};
|
||||
|
||||
/* stereo PCM selector */
|
||||
enum {
|
||||
MAD_PCM_CHANNEL_STEREO_LEFT = 0,
|
||||
MAD_PCM_CHANNEL_STEREO_RIGHT = 1
|
||||
};
|
||||
|
||||
void mad_synth_init(struct mad_synth *);
|
||||
|
||||
# define mad_synth_finish(synth) /* nothing */
|
||||
|
||||
void mad_synth_mute(struct mad_synth *);
|
||||
|
||||
void mad_synth_frame(struct mad_synth *, struct mad_frame const *);
|
||||
|
||||
# endif
|
||||
|
||||
/* Id: decoder.h,v 1.17 2004/01/23 09:41:32 rob Exp */
|
||||
|
||||
# ifndef LIBMAD_DECODER_H
|
||||
# define LIBMAD_DECODER_H
|
||||
|
||||
|
||||
enum mad_decoder_mode {
|
||||
MAD_DECODER_MODE_SYNC = 0,
|
||||
MAD_DECODER_MODE_ASYNC
|
||||
};
|
||||
|
||||
enum mad_flow {
|
||||
MAD_FLOW_CONTINUE = 0x0000, /* continue normally */
|
||||
MAD_FLOW_STOP = 0x0010, /* stop decoding normally */
|
||||
MAD_FLOW_BREAK = 0x0011, /* stop decoding and signal an error */
|
||||
MAD_FLOW_IGNORE = 0x0020 /* ignore the current frame */
|
||||
};
|
||||
|
||||
struct mad_decoder {
|
||||
enum mad_decoder_mode mode;
|
||||
|
||||
int options;
|
||||
|
||||
struct {
|
||||
long pid;
|
||||
int in;
|
||||
int out;
|
||||
} async;
|
||||
|
||||
struct {
|
||||
struct mad_stream stream;
|
||||
struct mad_frame frame;
|
||||
struct mad_synth synth;
|
||||
} *sync;
|
||||
|
||||
void *cb_data;
|
||||
|
||||
enum mad_flow (*input_func)(void *, struct mad_stream *);
|
||||
enum mad_flow (*header_func)(void *, struct mad_header const *);
|
||||
enum mad_flow (*filter_func)(void *,
|
||||
struct mad_stream const *, struct mad_frame *);
|
||||
enum mad_flow (*output_func)(void *,
|
||||
struct mad_header const *, struct mad_pcm *);
|
||||
enum mad_flow (*error_func)(void *, struct mad_stream *, struct mad_frame *);
|
||||
enum mad_flow (*message_func)(void *, void *, unsigned int *);
|
||||
};
|
||||
|
||||
void mad_decoder_init(struct mad_decoder *, void *,
|
||||
enum mad_flow (*)(void *, struct mad_stream *),
|
||||
enum mad_flow (*)(void *, struct mad_header const *),
|
||||
enum mad_flow (*)(void *,
|
||||
struct mad_stream const *,
|
||||
struct mad_frame *),
|
||||
enum mad_flow (*)(void *,
|
||||
struct mad_header const *,
|
||||
struct mad_pcm *),
|
||||
enum mad_flow (*)(void *,
|
||||
struct mad_stream *,
|
||||
struct mad_frame *),
|
||||
enum mad_flow (*)(void *, void *, unsigned int *));
|
||||
int mad_decoder_finish(struct mad_decoder *);
|
||||
|
||||
# define mad_decoder_options(decoder, opts) \
|
||||
((void) ((decoder)->options = (opts)))
|
||||
|
||||
int mad_decoder_run(struct mad_decoder *, enum mad_decoder_mode);
|
||||
int mad_decoder_message(struct mad_decoder *, void *, unsigned int *);
|
||||
|
||||
# endif
|
||||
|
||||
# ifdef __cplusplus
|
||||
}
|
||||
# endif
|
@ -3,3 +3,5 @@ cmake_minimum_required(VERSION 2.8.12)
|
||||
add_jslib(utils/class.js lib/utils/class ${LORGAR_DIR} browser)
|
||||
add_jslib(utils/subscribable.js lib/utils/subscribable ${LORGAR_DIR} browser)
|
||||
add_jslib(utils/globalMethods.js lib/utils/globalMethods ${LORGAR_DIR} browser)
|
||||
add_jslib(utils/enum.js lib/utils/enum ${LORGAR_DIR} browser)
|
||||
add_jslib(utils/stateMachine.js lib/utils/stateMachine ${LORGAR_DIR} browser)
|
||||
|
@ -16,4 +16,8 @@ add_jslib(wController/attributes.js lib/wController/attributes ${LORGAR_DIR} bro
|
||||
add_jslib(wController/localModel.js lib/wController/localModel ${LORGAR_DIR} browser)
|
||||
add_jslib(wController/imagePane.js lib/wController/imagePane ${LORGAR_DIR} browser)
|
||||
add_jslib(wController/file/file.js lib/wController/file/file ${LORGAR_DIR} browser)
|
||||
add_jslib(wController/file/audio.js lib/wController/file/audio ${LORGAR_DIR} browser)
|
||||
add_jslib(wController/image.js lib/wController/image ${LORGAR_DIR} browser)
|
||||
add_jslib(wController/button.js lib/wController/button ${LORGAR_DIR} browser)
|
||||
add_jslib(wController/player.js lib/wController/player ${LORGAR_DIR} browser)
|
||||
add_jslib(wController/imageById.js lib/wController/imageById ${LORGAR_DIR} browser)
|
||||
|
@ -1,5 +1,8 @@
|
||||
"use strict";
|
||||
(function main_js() {
|
||||
requirejs.config({
|
||||
"baseUrl": "/"
|
||||
});
|
||||
requirejs.onError = function(e) {
|
||||
throw e;
|
||||
}
|
||||
@ -8,6 +11,8 @@
|
||||
defineArray.push("test/test");
|
||||
defineArray.push("core/lorgar");
|
||||
defineArray.push("lib/utils/globalMethods");
|
||||
defineArray.push("lib/em/wrapper");
|
||||
|
||||
|
||||
require(defineArray, function main_module() {
|
||||
require("lib/utils/globalMethods");
|
||||
@ -17,6 +22,8 @@
|
||||
var Controller = require("lib/wController/controller");
|
||||
var View = require("views/view");
|
||||
|
||||
window.Mp3Decoder = Module.Decoder;
|
||||
|
||||
var waiter = {
|
||||
views: false,
|
||||
controllers: false,
|
||||
@ -32,12 +39,14 @@
|
||||
window.unregisterForeignController = window.lorgar.unregisterForeignController.bind(window.lorgar);
|
||||
window.subscribeForeignController = window.lorgar.subscribeForeignController.bind(window.lorgar);
|
||||
window.unsubscribeForeignController = window.lorgar.unsubscribeForeignController.bind(window.lorgar);
|
||||
window.play = window.lorgar.play.bind(window.lorgar);
|
||||
window.scheduleToPlay = window.lorgar.scheduleToPlay.bind(window.lorgar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Controller.initialize(["String", "List", "Vocabulary", "Page", "PanesList", "Link", "Image"], waiter.check.bind(waiter, "controllers"));
|
||||
View.initialize(["Label", "Page", "PanesList", "Nav", "Image"], waiter.check.bind(waiter, "views"));
|
||||
Controller.initialize(["String", "List", "Vocabulary", "Page", "PanesList", "Link", "Image", "Button"], waiter.check.bind(waiter, "controllers"));
|
||||
View.initialize(["Label", "Page", "PanesList", "Nav", "Image", "Button", "Enumeration"], waiter.check.bind(waiter, "views"));
|
||||
|
||||
var test = new Test();
|
||||
test.run();
|
||||
|
@ -11,5 +11,10 @@ configure_file(mainLayout.js mainLayout.js)
|
||||
configure_file(page.js page.js)
|
||||
configure_file(pane.js pane.js)
|
||||
configure_file(image.js image.js)
|
||||
configure_file(button.js button.js)
|
||||
configure_file(enumeration.js enumeration.js)
|
||||
configure_file(player.js player.js)
|
||||
configure_file(slider.js slider.js)
|
||||
configure_file(songProgress.js songProgress.js)
|
||||
|
||||
add_subdirectory(helpers)
|
||||
|
98
lorgar/views/button.js
Normal file
98
lorgar/views/button.js
Normal file
@ -0,0 +1,98 @@
|
||||
"use strict";
|
||||
(function() {
|
||||
var moduleName = "views/button";
|
||||
|
||||
var deps = [];
|
||||
deps.push("views/layout");
|
||||
deps.push("views/label");
|
||||
|
||||
define(moduleName, deps, function() {
|
||||
var Layout = require("views/layout");
|
||||
var Label = require("views/label");
|
||||
|
||||
var Button = Layout.inherit({
|
||||
"className": "Button",
|
||||
"constructor": function(controller, options) {
|
||||
var base = {
|
||||
padding: 5
|
||||
};
|
||||
W.extend(base, options)
|
||||
Layout.fn.constructor.call(this, controller, base);
|
||||
|
||||
this.addClass("hoverable");
|
||||
this.addClass("button");
|
||||
this._enabled = true;
|
||||
this._hasLabel = false;
|
||||
this._e.addEventListener("click", this._onClick.bind(this), false);
|
||||
|
||||
controller.on("setEnabled", this._onSetEnabled, this);
|
||||
controller.on("setLabel", this._onSetLabel, this);
|
||||
|
||||
this._onSetEnabled(controller.enabled);
|
||||
this._onSetLabel(controller.hasLabel, controller.label);
|
||||
},
|
||||
"destructor": function() {
|
||||
this._f.off("setEnabled", this._onSetEnabled, this);
|
||||
this._f.off("setLabel", this._onSetLabel, this);
|
||||
|
||||
Layout.fn.destructor.call(this);
|
||||
},
|
||||
"append": function(child, aligment, index) {
|
||||
this._updateLimits();
|
||||
|
||||
Layout.fn.append.call(this, child, aligment, index);
|
||||
},
|
||||
"_onChildChangeLimits": function(child) {
|
||||
this._updateLimits();
|
||||
|
||||
Layout.fn._onChildChangeLimits.call(this, child);
|
||||
},
|
||||
"_onClick": function(e) {
|
||||
if (this._enabled) {
|
||||
this._f.activate();
|
||||
}
|
||||
e.stopPropagation();
|
||||
},
|
||||
"_onSetEnabled": function(enabled) {
|
||||
if (this._enabled !== enabled) {
|
||||
this._enabled = enabled;
|
||||
if (this._enabled) {
|
||||
this.removeClass("disabled");
|
||||
} else {
|
||||
this.addClass("disabled");
|
||||
}
|
||||
}
|
||||
},
|
||||
"_onSetLabel": function(hasLabel, label) {
|
||||
if (this._hasLabel !== hasLabel) {
|
||||
this._hasLabel = hasLabel;
|
||||
if (this._hasLabel) {
|
||||
this._label = new Label(label);
|
||||
this.append(this._label, Layout.Aligment.CenterCenter);
|
||||
} else {
|
||||
this._label.destructor();
|
||||
delete this._label();
|
||||
}
|
||||
}
|
||||
},
|
||||
"_updateLimits": function() {
|
||||
var minWidth = this._o.padding * 2;
|
||||
var maxWidth = this._o.padding * 2;
|
||||
var minHeight = this._o.padding * 2;
|
||||
var maxHeight = this._o.padding * 2;
|
||||
|
||||
if (this._hasLabel) {
|
||||
minWidth += this._label._o.minWidth;
|
||||
minHeight += this._label._o.minHeight;
|
||||
maxWidth += this._label._o.maxWidth;
|
||||
maxHeight += this._label._o.maxHeight;
|
||||
}
|
||||
|
||||
this._setLimits(minWidth, minHeight, maxWidth, maxHeight);
|
||||
}
|
||||
});
|
||||
|
||||
return Button;
|
||||
})
|
||||
})();
|
||||
|
53
lorgar/views/enumeration.js
Normal file
53
lorgar/views/enumeration.js
Normal file
@ -0,0 +1,53 @@
|
||||
"use strict";
|
||||
(function view_enumeration_js() {
|
||||
var moduleName = "views/enumeration";
|
||||
|
||||
var deps = [];
|
||||
deps.push("views/gridLayout");
|
||||
deps.push("views/label");
|
||||
deps.push("lib/wController/localModel");
|
||||
|
||||
define(moduleName, deps, function view_enumeration_module() {
|
||||
var GridLayout = require("views/gridLayout");
|
||||
var Label = require("views/label");
|
||||
var LocalModel = require("lib/wController/localModel");
|
||||
|
||||
var Enumeration = GridLayout.inherit({
|
||||
className: "Enumeration",
|
||||
constructor: function(controller, options) {
|
||||
var base = {};
|
||||
W.extend(base, options)
|
||||
|
||||
this._lm = new LocalModel();
|
||||
GridLayout.fn.constructor.call(this, controller, base);
|
||||
|
||||
this._lv = new Label(this._lm);
|
||||
this.append(this._lv, 0, 0, 1, 1, GridLayout.Aligment.CenterCenter);
|
||||
|
||||
this._uncyclic.push(this._lm.destructor.bind(this._lm));
|
||||
},
|
||||
_onData: function() {
|
||||
if (this._f.initialized) {
|
||||
var e = this._f.enum;
|
||||
var value = this._f.data;
|
||||
var title;
|
||||
if (e.hasAdditional("title")) {
|
||||
title = e.additional[value].title;
|
||||
} else {
|
||||
title = e.reversed[value];
|
||||
}
|
||||
|
||||
var desc = "";
|
||||
if (e.hasAdditional("description")) {
|
||||
desc = e.additional[value].description;
|
||||
}
|
||||
|
||||
this._lm.setData(title);
|
||||
this._e.setAttribute("title", desc);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return Enumeration;
|
||||
})
|
||||
})();
|
@ -250,20 +250,52 @@
|
||||
var target = pos + span;
|
||||
var minSize = 0;
|
||||
var maxSize = 0;
|
||||
var flexibleColls = [];
|
||||
for (j = pos; j < target; ++j) {
|
||||
minSize += this._cols[j].min;
|
||||
maxSize += this._cols[j].max;
|
||||
}
|
||||
if (e.child._o.minWidth > minSize) {
|
||||
var portion = (e.child._o.minWidth - minSize) / span;
|
||||
for (j = pos; j < target; ++j) {
|
||||
this._cols[j].min += portion;
|
||||
if (this._cols[j].min < this._cols[j].max) {
|
||||
flexibleColls.push(this._cols[j]);
|
||||
}
|
||||
}
|
||||
if (e.child._o.maxWidth < maxSize) {
|
||||
var portion = (maxSize - e.child._o.maxWidth) / span;
|
||||
for (j = pos; j < target; ++j) {
|
||||
this._cols[j].max -= portion;
|
||||
|
||||
var leftMin = e.child._o.minWidth - minSize;
|
||||
if (leftMin > 0) {
|
||||
while (leftMin > 0 && flexibleColls.length > 0) {
|
||||
var portion = leftMin / flexibleColls.length;
|
||||
|
||||
for (j = 0; j < flexibleColls.length; ++j) {
|
||||
var lPortion = Math.min(flexibleColls[j].max, portion);
|
||||
flexibleColls[j].min += lPortion;
|
||||
leftMin -= lPortion;
|
||||
if (flexibleColls[j].min === flexibleColls[j].max) {
|
||||
flexibleColls.splice(j, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (leftMin < 1) {
|
||||
leftMin = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
var leftMax = maxSize - e.child._o.maxWidth
|
||||
if (leftMax > 0) {
|
||||
while (leftMax > 0 && flexibleColls.length > 0) {
|
||||
var portion = leftMax / flexibleColls.length;
|
||||
|
||||
for (j = 0; j < flexibleColls.length; ++j) {
|
||||
var lPortion = Math.max(flexibleColls[j].min, portion);
|
||||
flexibleColls[j].max -= lPortion;
|
||||
leftMax -= lPortion;
|
||||
if (flexibleColls[j].min === flexibleColls[j].max) {
|
||||
flexibleColls.splice(j, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (leftMax < 1) {
|
||||
leftMax = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -275,20 +307,52 @@
|
||||
var target = pos + span;
|
||||
var minSize = 0;
|
||||
var maxSize = 0;
|
||||
var flexibleRows = [];
|
||||
for (j = pos; j < target; ++j) {
|
||||
minSize += this._rows[j].min;
|
||||
maxSize += this._rows[j].max;
|
||||
}
|
||||
if (e.child._o.minHeight > minSize) {
|
||||
var portion = (e.child._o.minHeight - minSize) / span;
|
||||
for (j = pos; j < target; ++j) {
|
||||
this._rows[j].min += portion;
|
||||
if (this._rows[j].min < this._rows[j].max) {
|
||||
flexibleRows.push(this._rows[j]);
|
||||
}
|
||||
}
|
||||
if (e.child._o.maxHeight < maxSize) {
|
||||
var portion = (maxSize - e.child._o.maxHeight) / span;
|
||||
for (j = pos; j < target; ++j) {
|
||||
this._rows[j].max -= portion;
|
||||
var leftMin = e.child._o.minHeigh - minSize;
|
||||
if (leftMin > 0) {
|
||||
while (leftMin > 0 && flexibleRows.length > 0) {
|
||||
var portion = leftMin / flexibleRows.length;
|
||||
|
||||
for (j = 0; j < flexibleRows.length; ++j) {
|
||||
var lPortion = Math.min(flexibleRows[j].max, portion);
|
||||
flexibleRows[j].min += lPortion;
|
||||
leftMin -= lPortion;
|
||||
if (flexibleRows[j].min === flexibleRows[j].max) {
|
||||
flexibleRows.splice(j, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (leftMin < 1) {
|
||||
leftMin = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
var leftMax = maxSize - e.child._o.maxHeigh
|
||||
if (leftMax > 0) {
|
||||
while (leftMax > 0 && flexibleRows.length > 0) {
|
||||
var portion = leftMax / flexibleRows.length;
|
||||
|
||||
for (j = 0; j < flexibleRows.length; ++j) {
|
||||
var lPortion = Math.max(flexibleRows[j].min, portion);
|
||||
flexibleRows[j].max -= lPortion;
|
||||
leftMax -= lPortion;
|
||||
if (flexibleRows[j].min === flexibleRows[j].max) {
|
||||
flexibleRows.splice(j, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (leftMax < 1) {
|
||||
leftMax = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,9 +30,9 @@
|
||||
this._y = 0;
|
||||
|
||||
this._e.addEventListener("mousedown", this._proxy.onMouseDown, false);
|
||||
this._e.addEventListener("touchstart", this._touch, false);
|
||||
this._e.addEventListener("touchmove", this._touch, false);
|
||||
this._e.addEventListener("touchend", this._touch, false);
|
||||
this._e.addEventListener("touchstart", W.touchToMouse, false);
|
||||
this._e.addEventListener("touchmove", W.touchToMouse, false);
|
||||
this._e.addEventListener("touchend", W.touchToMouse, false);
|
||||
},
|
||||
"destructor": function () {
|
||||
if (this._dragging) {
|
||||
@ -45,9 +45,9 @@
|
||||
}
|
||||
|
||||
this._e.removeEventListener("mousedown", this._proxy.onMouseDown);
|
||||
this._e.removeEventListener("touchstart", this._touch);
|
||||
this._e.removeEventListener("touchmove", this._touch);
|
||||
this._e.removeEventListener("touchend", this._touch);
|
||||
this._e.removeEventListener("touchstart", W.touchToMouse);
|
||||
this._e.removeEventListener("touchmove", W.touchToMouse);
|
||||
this._e.removeEventListener("touchend", W.touchToMouse);
|
||||
|
||||
Subscribable.fn.destructor.call(this);
|
||||
},
|
||||
@ -105,41 +105,6 @@
|
||||
this._v.trigger("dragEnd");
|
||||
return false;
|
||||
}
|
||||
},
|
||||
"_touch": function (e) {
|
||||
e.preventDefault();
|
||||
if (e.touches.length > 1 || (e.type == "touchend" && e.touches.length > 0))
|
||||
return;
|
||||
|
||||
var type = null;
|
||||
var touch = null;
|
||||
var src = e.currentTarget;
|
||||
switch (e.type) {
|
||||
case "touchstart":
|
||||
type = "mousedown";
|
||||
touch = e.changedTouches[0];
|
||||
|
||||
break;
|
||||
case "touchmove":
|
||||
type = "mousemove";
|
||||
touch = e.changedTouches[0];
|
||||
src = window;
|
||||
break;
|
||||
case "touchend":
|
||||
type = "mouseup";
|
||||
touch = e.changedTouches[0];
|
||||
src = window;
|
||||
break;
|
||||
}
|
||||
|
||||
var event = new MouseEvent(type, {
|
||||
button: 0,
|
||||
screenX: touch.screenX,
|
||||
screenY: touch.screenY,
|
||||
clientX: touch.clientX,
|
||||
clientY: touch.clientY
|
||||
});
|
||||
src.dispatchEvent(event);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -76,8 +76,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (this._w !== undefined && this._h !== undefined) {
|
||||
child.setSize(this._w, this._h);
|
||||
index = index || this._c.length - 1;
|
||||
var c = this._c[index];
|
||||
this._positionElement(c);
|
||||
}
|
||||
},
|
||||
"clear": function() {
|
||||
|
@ -5,19 +5,46 @@
|
||||
var defineArray = [];
|
||||
defineArray.push("views/gridLayout");
|
||||
defineArray.push("views/label");
|
||||
defineArray.push("views/view");
|
||||
defineArray.push("views/navigationPanel");
|
||||
defineArray.push("views/layout");
|
||||
defineArray.push("views/enumeration");
|
||||
defineArray.push("views/player");
|
||||
defineArray.push("lib/wController/localModel");
|
||||
|
||||
define(moduleName, defineArray, function mainLayout_module() {
|
||||
var GridLayout = require("views/gridLayout");
|
||||
var ViewLabel = require("views/label");
|
||||
var View = require("views/view");
|
||||
var ViewNavigationPanel = require("views/navigationPanel");
|
||||
var Layout = require("views/layout");
|
||||
var Enumeration = require("views/enumeration");
|
||||
var Player = require("views/player");
|
||||
var LocalModel = require("lib/wController/localModel");
|
||||
|
||||
var MainLayout = GridLayout.inherit({
|
||||
"className": "MainLayout",
|
||||
"constructor": function(controller, options) {
|
||||
GridLayout.fn.constructor.call(this, controller, options);
|
||||
|
||||
this._statusBarPosition = 2;
|
||||
this._player = undefined;
|
||||
|
||||
this._mainColorHelper = new LocalModel({backgroundColor: "mainColor"});
|
||||
this._statusBarModel = new LocalModel({backgroundColor: "secondaryColor"});
|
||||
|
||||
this._uncyclic.push(this._statusBarModel.destructor.bind(this._statusBarModel));
|
||||
this._uncyclic.push(this._mainColorHelper.destructor.bind(this._mainColorHelper));
|
||||
|
||||
var spacerL = new View(this._mainColorHelper, {maxWidth: 50});
|
||||
var spacerR = new View(this._mainColorHelper, {maxWidth: 50});
|
||||
this.append(spacerL, 1, 0, 1, 1);
|
||||
this.append(spacerR, 1, 2, 1, 1);
|
||||
|
||||
this._statusBar = new GridLayout(this._statusBarModel);
|
||||
this._statusBar.append(new View(this._statusBarModel), 0, 1, 1, 1);
|
||||
this.append(this._statusBar, 3, 0, 1, 3);
|
||||
},
|
||||
"_onNewController": function(controller) {
|
||||
GridLayout.fn._onNewController.call(this, controller);
|
||||
|
||||
@ -25,13 +52,8 @@
|
||||
|
||||
switch (controller.name) {
|
||||
case "version":
|
||||
var lm = new LocalModel({
|
||||
backgroundColor: "secondaryColor"
|
||||
});
|
||||
var lay = new Layout(lm, {maxHeight: 15})
|
||||
view = new ViewLabel(controller);
|
||||
lay.append(view, Layout.Aligment.RightCenter);
|
||||
this.append(lay, 2, 0, 1, 3);
|
||||
this._statusBar.append(view, 0, 0, 1, 1, Layout.Aligment.LeftCenter);
|
||||
break;
|
||||
case "navigationPanel":
|
||||
view = new ViewNavigationPanel(controller);
|
||||
@ -43,6 +65,31 @@
|
||||
//this.trigger("serviceMessage", "Unsupported view: " + name + " (" + type + ")", 1);
|
||||
break;
|
||||
}
|
||||
},
|
||||
addState: function(name, state) {
|
||||
var lm = new LocalModel({fontFamily: "casualFont"});
|
||||
lm.setData(name + ": ");
|
||||
var lv = new ViewLabel(lm);
|
||||
var e = new Enumeration(state);
|
||||
this._statusBar.append(lv, 0, this._statusBarPosition++, 1, 1, Layout.Aligment.LeftCenter);
|
||||
this._statusBar.append(e, 0, this._statusBarPosition++, 1, 1, Layout.Aligment.LeftCenter);
|
||||
|
||||
this._uncyclic.push(lm.destructor.bind(lm));
|
||||
},
|
||||
appendPlayer: function(playerModel) {
|
||||
if (this._player !== undefined) {
|
||||
throw new Error("An attempt to add player to main layout for the second time");
|
||||
}
|
||||
this._player = new Player(playerModel);
|
||||
this.append(this._player, 2, 0, 1, 3);
|
||||
},
|
||||
removePlayer: function() {
|
||||
if (this._player === undefined) {
|
||||
throw new Error("An attempt to remove non existing player from mainLayout");
|
||||
}
|
||||
this.removeChild(this._player);
|
||||
this._player.destructor();
|
||||
this._player = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
defineArray.push("views/layout");
|
||||
defineArray.push("views/label");
|
||||
defineArray.push("views/image");
|
||||
defineArray.push("views/button");
|
||||
defineArray.push("lib/wController/localModel");
|
||||
|
||||
define(moduleName, defineArray, function() {
|
||||
@ -15,6 +16,7 @@
|
||||
var Layout = require("views/layout");
|
||||
var Label = require("views/label");
|
||||
var Image = require("views/image");
|
||||
var Button = require("views/button");
|
||||
var LM = require("lib/wController/localModel");
|
||||
|
||||
var Pane = Layout.inherit({
|
||||
@ -25,6 +27,7 @@
|
||||
W.extend(base, options);
|
||||
Layout.fn.constructor.call(this, controller, options);
|
||||
|
||||
this._aCount = 0;
|
||||
this._initProxy();
|
||||
this.addClass("hoverable");
|
||||
this._e.addEventListener("click", this._proxy.onClick, false);
|
||||
@ -46,6 +49,12 @@
|
||||
|
||||
this._f.on("newElement", this._onNewElement, this);
|
||||
this._f.on("removeElement", this._onRemoveElement, this);
|
||||
this._f.on("addAction", this._onAddAction, this);
|
||||
|
||||
var acts = this._f.getActions();
|
||||
for (var i = 0; i < acts.length; ++i) {
|
||||
this._onAddAction(acts[i]);
|
||||
}
|
||||
|
||||
this._uncyclic.push(function() {
|
||||
lm.destructor();
|
||||
@ -56,6 +65,31 @@
|
||||
|
||||
Layout.fn.destructor.call(this);
|
||||
},
|
||||
"_onAddAction": function(model) {
|
||||
var alignment;
|
||||
switch (this._aCount) {
|
||||
case 0:
|
||||
alignment = Layout.Aligment.LeftTop;
|
||||
break;
|
||||
case 1:
|
||||
alignment = Layout.Aligment.RightTop;
|
||||
break;
|
||||
case 2:
|
||||
alignment = Layout.Aligment.RightBottom;
|
||||
break;
|
||||
case 3:
|
||||
alignment = Layout.Aligment.LeftBottom;
|
||||
break;
|
||||
default:
|
||||
console.warn("Pane can't place more then 4 action, ignoring");
|
||||
break
|
||||
}
|
||||
if (alignment !== undefined) {
|
||||
var view = new Button(model);
|
||||
this.append(view, alignment);
|
||||
this._aCount++;
|
||||
}
|
||||
},
|
||||
"_applyProperties": function() {
|
||||
this._onAddProperty("secondaryColor", "background");
|
||||
|
||||
@ -67,8 +101,8 @@
|
||||
};
|
||||
},
|
||||
"_onClick": function() {
|
||||
if (this._f.data.at("hasPageLink").valueOf() === true) {
|
||||
this.trigger("activate", this._f.data.at("pageLink").clone());
|
||||
if (this._f.hasPageLink()) {
|
||||
this.trigger("activate", this._f.getPageLink());
|
||||
}
|
||||
},
|
||||
"_onLabelChangeLimits": function(label) {
|
||||
|
@ -50,15 +50,13 @@
|
||||
this._f.setSubscriptionRange(0, 0);
|
||||
},
|
||||
"append": function(child, index) {
|
||||
var model = new LM();
|
||||
var nest = new Layout(model, {
|
||||
var nest = new Layout(helper, {
|
||||
minHeight: this._o.nestHeight,
|
||||
maxHeight: this._o.nestHeight,
|
||||
minWidth: this._o.nestWidth,
|
||||
minWidth: this._o.nestWidth,
|
||||
scrollable: Layout.Scroll.None
|
||||
});
|
||||
nest._uncyclic.push(function() {model.destructor()});
|
||||
nest.append(child);
|
||||
child.on("activate", this._onChildActivate, this); //todo need to remove handler on deletion
|
||||
this._addChild(nest, 0, index);
|
||||
@ -184,6 +182,8 @@
|
||||
}
|
||||
});
|
||||
|
||||
var helper = new LM();
|
||||
|
||||
return PanesList;
|
||||
});
|
||||
})();
|
||||
|
163
lorgar/views/player.js
Normal file
163
lorgar/views/player.js
Normal file
@ -0,0 +1,163 @@
|
||||
"use strict";
|
||||
(function() {
|
||||
var moduleName = "views/player"
|
||||
|
||||
var deps = [];
|
||||
deps.push("views/gridLayout");
|
||||
deps.push("views/button");
|
||||
deps.push("views/label");
|
||||
deps.push("views/view");
|
||||
deps.push("views/image");
|
||||
deps.push("views/songProgress");
|
||||
deps.push("views/slider");
|
||||
|
||||
deps.push("lib/wController/localModel");
|
||||
|
||||
deps.push("lib/utils/enum");
|
||||
|
||||
define(moduleName, deps, function() {
|
||||
var GridLayout = require("views/gridLayout");
|
||||
var Button = require("views/button");
|
||||
var Label = require("views/label");
|
||||
var View = require("views/view");
|
||||
var Image = require("views/image");
|
||||
var SongProgress = require("views/songProgress");
|
||||
var Slider = require("views/slider");
|
||||
|
||||
var Model = require("lib/wController/localModel");
|
||||
|
||||
var Enum = require("lib/utils/enum");
|
||||
|
||||
var Player = GridLayout.inherit({
|
||||
className: "Player",
|
||||
constructor: function(ctrl, options) {
|
||||
GridLayout.fn.constructor.call(this, ctrl, options);
|
||||
|
||||
this._playPause = null;
|
||||
this._prev = null;
|
||||
this._next = null;
|
||||
this._picture = null;
|
||||
this._cpbCtrl = null;
|
||||
|
||||
this._infoModels = {
|
||||
artist: new Model(null, "artist: unknown"),
|
||||
album: new Model(null, "album: unknown"),
|
||||
song: new Model(null, "song: unknown")
|
||||
}
|
||||
|
||||
ctrl.on("newElement", this._onNewElement, this);
|
||||
ctrl.on("removeElement", this._onRemoveElement, this);
|
||||
|
||||
var artist = new Label(this._infoModels.artist);
|
||||
var album = new Label(this._infoModels.album);
|
||||
var song = new Label(this._infoModels.song);
|
||||
var spacer = new View(helper, {maxWidth: 10});
|
||||
var progress = new SongProgress(ctrl.progress);
|
||||
var volume = new Slider(ctrl.volume, {maxWidth: 100});
|
||||
|
||||
this.append(artist, 0, 4, 1, 1, GridLayout.Aligment.LeftCenter);
|
||||
this.append(song, 1, 4, 1, 1, GridLayout.Aligment.LeftCenter);
|
||||
this.append(album, 2, 4, 1, 1, GridLayout.Aligment.LeftCenter);
|
||||
this.append(spacer, 0, 6, 3, 1, GridLayout.Aligment.LeftCenter);
|
||||
this.append(progress, 1, 5, 1, 1, GridLayout.Aligment.CenterCenter);
|
||||
this.append(volume, 1, 7, 1, 1, GridLayout.Aligment.CenterCenter);
|
||||
|
||||
this._uncyclic.push(this._infoModels.artist.destructor.bind(this._infoModels.artist));
|
||||
this._uncyclic.push(this._infoModels.song.destructor.bind(this._infoModels.song));
|
||||
this._uncyclic.push(this._infoModels.album.destructor.bind(this._infoModels.album));
|
||||
},
|
||||
destructor: function() {
|
||||
this._f.off("newElement", this._onNewElement, this);
|
||||
this._f.off("removeElement", this._onRemoveElement, this);
|
||||
this._clearCpbCtrl();
|
||||
|
||||
GridLayout.fn.destructor.call(this);
|
||||
},
|
||||
_clearCpbCtrl: function() {
|
||||
if (this._cpbCtrl !== null) {
|
||||
this._cpbCtrl.off("newElement", this._onCpbNewElement, this);
|
||||
this._cpbCtrl.off("removeElement", this._onCpbRemoveElement, this);
|
||||
this._cpbCtrl = null;
|
||||
}
|
||||
},
|
||||
_onNewElement: function(ctrl, type) {
|
||||
var ItemType = Enum.storage["ItemType"];
|
||||
|
||||
switch (type) {
|
||||
case ItemType.straight.playPause:
|
||||
this._playPause = new Button(ctrl);
|
||||
this.append(this._playPause, 0, 2, 3, 1);
|
||||
break;
|
||||
case ItemType.straight.prev:
|
||||
this._prev = new Button(ctrl);
|
||||
this.append(this._prev, 0, 1, 3, 1);
|
||||
break;
|
||||
case ItemType.straight.next:
|
||||
this._next = new Button(ctrl);
|
||||
this.append(this._next, 0, 3, 3, 1);
|
||||
break;
|
||||
case ItemType.straight.queue:
|
||||
break;
|
||||
case ItemType.straight.currentPlayback:
|
||||
this._clearCpbCtrl();
|
||||
this._cpbCtrl = ctrl;
|
||||
this._cpbCtrl.on("newElement", this._onCpbNewElement, this);
|
||||
this._cpbCtrl.on("removeElement", this._onCpbRemoveElement, this);
|
||||
break;
|
||||
case ItemType.straight.picture:
|
||||
this._picture = new Image(ctrl, {
|
||||
maxWidth: 100,
|
||||
maxHeight: 100
|
||||
});
|
||||
this.append(this._picture, 0, 0, 3, 1);
|
||||
break;
|
||||
}
|
||||
},
|
||||
_onRemoveElement: function(type) {
|
||||
var ItemType = Enum.storage["ItemType"];
|
||||
|
||||
switch (type) {
|
||||
case ItemType.straight.playPause:
|
||||
this._playPause.destructor();
|
||||
this._playPause = null;
|
||||
break;
|
||||
case ItemType.straight.prev:
|
||||
this._prev.destructor();
|
||||
this._prev = null;
|
||||
break;
|
||||
case ItemType.straight.next:
|
||||
this._next.destructor();
|
||||
this._next = null;
|
||||
break;
|
||||
case ItemType.straight.queue:
|
||||
break;
|
||||
case ItemType.straight.currentPlayback:
|
||||
this._clearCpbCtrl();
|
||||
break;
|
||||
case ItemType.straight.picture:
|
||||
this._picture.destructor();
|
||||
this._picture = null;
|
||||
break;
|
||||
}
|
||||
},
|
||||
_onCpbNewElement: function(key, value) {
|
||||
var model = this._infoModels[key];
|
||||
|
||||
if (model) {
|
||||
model.setData(key + ": " + value.toString());
|
||||
}
|
||||
},
|
||||
_onCpbRemoveElement: function(key) {
|
||||
var model = this._infoModels[key];
|
||||
|
||||
if (model) {
|
||||
model.setData(key + ": unknown");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var helper = new Model();
|
||||
|
||||
return Player;
|
||||
});
|
||||
})()
|
142
lorgar/views/slider.js
Normal file
142
lorgar/views/slider.js
Normal file
@ -0,0 +1,142 @@
|
||||
"use strict";
|
||||
(function() {
|
||||
var moduleName = "views/slider";
|
||||
|
||||
var deps = [];
|
||||
deps.push("views/view");
|
||||
|
||||
define(moduleName, deps, function() {
|
||||
var View = require("views/view");
|
||||
|
||||
var Slider = View.inherit({
|
||||
className: "Slider",
|
||||
constructor: function(controller, options) {
|
||||
var base = {
|
||||
minHeight: 10,
|
||||
maxHeight: 10
|
||||
};
|
||||
W.extend(base, options)
|
||||
var el = document.createElement("div");
|
||||
this._createBars();
|
||||
View.fn.constructor.call(this, controller, base, el);
|
||||
|
||||
this._seeking = false;
|
||||
this._createProxy();
|
||||
|
||||
this._f.on("value", this._onValue, this);
|
||||
this._f.on("enabled", this._onEnabled, this);
|
||||
|
||||
this._e.style.backgroundColor = "#eeeeee";
|
||||
this._e.appendChild(this._value);
|
||||
|
||||
if (this._f.enabled) {
|
||||
this._e.addEventListener("mousedown", this._proxy.onMouseDown, false);
|
||||
this._e.addEventListener("touchstart", W.touchToMouse, false);
|
||||
this._e.addEventListener("touchmove", W.touchToMouse, false);
|
||||
this._e.addEventListener("touchend", W.touchToMouse, false);
|
||||
}
|
||||
},
|
||||
destructor: function() {
|
||||
if (this._seeking) {
|
||||
window.removeEventListener("mouseup", this._proxy.onMouseUp);
|
||||
window.removeEventListener("mousemove", this._proxy.onMouseMove);
|
||||
lorgar._body.removeClass("dragging");
|
||||
lorgar._body.removeClass("non-selectable");
|
||||
}
|
||||
if (this._f.enabled) {
|
||||
this._e.removeEventListener("mousedown", this._proxy.onMouseDown);
|
||||
this._e.removeEventListener("touchstart", W.touchToMouse);
|
||||
this._e.removeEventListener("touchmove", W.touchToMouse);
|
||||
this._e.removeEventListener("touchend", W.touchToMouse);
|
||||
}
|
||||
|
||||
this._f.off("value", this._value, this);
|
||||
this._f.off("enabled", this._onEnabled, this);
|
||||
|
||||
View.fn.destructor.call(this);
|
||||
},
|
||||
_createBars: function() {
|
||||
this._value = document.createElement("div");
|
||||
|
||||
this._value.style.backgroundColor = View.theme.primaryColor || "#ff0000";
|
||||
this._value.style.height = "100%";
|
||||
this._value.style.width = "0";
|
||||
this._value.style.position = "absolute";
|
||||
this._value.style.top = "0";
|
||||
this._value.style.left = "0";
|
||||
},
|
||||
_createProxy: function () {
|
||||
this._proxy = {
|
||||
onMouseDown: this._onMouseDown.bind(this),
|
||||
onMouseUp: this._onMouseUp.bind(this),
|
||||
onMouseMove: this._onMouseMove.bind(this)
|
||||
}
|
||||
},
|
||||
_onData: function() {
|
||||
this._onValue(this._f.value);
|
||||
},
|
||||
_onEnabled: function(enabled) {
|
||||
if (enabled) {
|
||||
this._e.addEventListener("mousedown", this._proxy.onMouseDown, false);
|
||||
this._e.addEventListener("touchstart", W.touchToMouse, false);
|
||||
this._e.addEventListener("touchmove", W.touchToMouse, false);
|
||||
this._e.addEventListener("touchend", W.touchToMouse, false);
|
||||
} else {
|
||||
if (this._seeking) {
|
||||
this._onMouseUp();
|
||||
}
|
||||
|
||||
this._e.removeEventListener("mousedown", this._proxy.onMouseDown);
|
||||
this._e.removeEventListener("touchstart", W.touchToMouse);
|
||||
this._e.removeEventListener("touchmove", W.touchToMouse);
|
||||
this._e.removeEventListener("touchend", W.touchToMouse);
|
||||
}
|
||||
},
|
||||
_onMouseDown: function(e) {
|
||||
if (e.which === 1) {
|
||||
window.addEventListener("mouseup", this._proxy.onMouseUp);
|
||||
window.addEventListener("mousemove", this._proxy.onMouseMove);
|
||||
lorgar._body.addClass("dragging");
|
||||
lorgar._body.addClass("non-selectable");
|
||||
this._seeking = true;
|
||||
|
||||
this._ap = this.getAbsolutePosition();
|
||||
var seek = Math.max(Math.min(e.pageX - this._ap.x, this._w), 0);
|
||||
var nSeek = seek / this._w;
|
||||
if (this._seek !== nSeek) {
|
||||
this._seek = nSeek;
|
||||
this._f.setValue(this._seek);
|
||||
}
|
||||
}
|
||||
},
|
||||
_onMouseMove: function(e) {
|
||||
var seek = Math.max(Math.min(e.pageX - this._ap.x, this._w), 0);
|
||||
var nSeek = seek / this._w;
|
||||
if (this._seek !== nSeek) {
|
||||
this._seek = nSeek;
|
||||
this._f.setValue(this._seek);
|
||||
}
|
||||
},
|
||||
_onMouseUp: function() {
|
||||
delete this._ap;
|
||||
delete this._seek;
|
||||
|
||||
this._seeking = false;
|
||||
window.removeEventListener("mouseup", this._proxy.onMouseUp);
|
||||
window.removeEventListener("mousemove", this._proxy.onMouseMove);
|
||||
lorgar._body.removeClass("dragging");
|
||||
},
|
||||
_onValue: function(pb) {
|
||||
this._value.style.width = pb * 100 + "%";
|
||||
},
|
||||
_resetTheme: function() {
|
||||
View.fn._resetTheme.call(this);
|
||||
|
||||
this._value.style.backgroundColor = View.theme.primaryColor || "#ff0000";
|
||||
}
|
||||
});
|
||||
|
||||
return Slider;
|
||||
})
|
||||
})();
|
||||
|
70
lorgar/views/songProgress.js
Normal file
70
lorgar/views/songProgress.js
Normal file
@ -0,0 +1,70 @@
|
||||
"use strict";
|
||||
(function() {
|
||||
var moduleName = "views/songProgress";
|
||||
|
||||
var deps = [];
|
||||
deps.push("views/slider");
|
||||
|
||||
define(moduleName, deps, function() {
|
||||
var Slider = require("views/slider");
|
||||
|
||||
var SongProgress = Slider.inherit({
|
||||
className: "SongProgress",
|
||||
constructor: function(controller, options) {
|
||||
var base = {
|
||||
minHeight: 10,
|
||||
maxHeight: 10
|
||||
};
|
||||
W.extend(base, options)
|
||||
Slider.fn.constructor.call(this, controller, base);
|
||||
|
||||
this._f.on("load", this._onLoad, this);
|
||||
|
||||
this._e.insertBefore(this._loadProgress, this._value);
|
||||
},
|
||||
destructor: function() {
|
||||
this._f.off("load", this._onLoad, this);
|
||||
|
||||
Slider.fn.destructor.call(this);
|
||||
},
|
||||
_createBars: function() {
|
||||
Slider.fn._createBars.call(this);
|
||||
|
||||
this._loadProgress = document.createElement("div");
|
||||
|
||||
this._loadProgress.style.backgroundColor = Slider.theme.secondaryColor || "#ffff00";
|
||||
this._loadProgress.style.height = "100%";
|
||||
this._loadProgress.style.width = "0";
|
||||
this._loadProgress.style.position = "absolute";
|
||||
this._loadProgress.style.top = "0";
|
||||
this._loadProgress.style.left = "0";
|
||||
},
|
||||
_onData: function() {
|
||||
Slider.fn._onData.call(this);
|
||||
this._onLoad(this._f.load);
|
||||
},
|
||||
_onLoad: function(load) {
|
||||
this._loadProgress.style.width = load * 100 + "%";
|
||||
},
|
||||
_onMouseDown: function(e) {
|
||||
if (e.which === 1) {
|
||||
this._f.trigger("seekingStart");
|
||||
}
|
||||
|
||||
Slider.fn._onMouseDown.call(this, e);
|
||||
},
|
||||
_onMouseUp: function(e) {
|
||||
Slider.fn._onMouseUp.call(this, e);
|
||||
this._f.trigger("seekingEnd", this._f.value);
|
||||
},
|
||||
_resetTheme: function() {
|
||||
Slider.fn._resetTheme.call(this);
|
||||
|
||||
this._loadProgress.style.backgroundColor = Slider.theme.secondaryColor || "#ffff00"
|
||||
}
|
||||
});
|
||||
|
||||
return SongProgress;
|
||||
})
|
||||
})();
|
||||
|
@ -54,7 +54,9 @@
|
||||
for (var i = 0; i < this._f._controllers.length; ++i) {
|
||||
this._onNewController(this._f._controllers[i]);
|
||||
}
|
||||
this._onData(this._f);
|
||||
//if (this._f.initialized) {
|
||||
this._onData(this._f);
|
||||
//}
|
||||
|
||||
View.collection[this._id] = this;
|
||||
this._applyProperties();
|
||||
@ -76,11 +78,7 @@
|
||||
Subscribable.fn.destructor.call(this);
|
||||
},
|
||||
"addClass": function(className) {
|
||||
var arr = this._e.className.split(" ");
|
||||
if (arr.indexOf(className) === -1) {
|
||||
arr.push(className);
|
||||
this._e.className = arr.join(" ");
|
||||
}
|
||||
this._e.classList.add(className);
|
||||
},
|
||||
"_applyProperties": function() {
|
||||
for (var i = 0; i < this._f.properties.length; ++i) {
|
||||
@ -100,6 +98,21 @@
|
||||
|
||||
return w;
|
||||
},
|
||||
"getAbsolutePosition": function() {
|
||||
var pp;
|
||||
if (this._p) {
|
||||
pp = this._p.getAbsolutePosition();
|
||||
} else {
|
||||
pp = Object.create(null);
|
||||
pp.x = 0;
|
||||
pp.y = 0;
|
||||
}
|
||||
|
||||
pp.x += this._x;
|
||||
pp.y += this._y;
|
||||
|
||||
return pp;
|
||||
},
|
||||
"_initDraggable": function() {
|
||||
this._dg = new Draggable(this, {
|
||||
snapDistance: this._o.snapDistance
|
||||
@ -136,20 +149,11 @@
|
||||
"remove": function() {
|
||||
if (this._p) {
|
||||
this._p.removeChild(this);
|
||||
delete this._p; //just to make sure
|
||||
}
|
||||
},
|
||||
"removeClass": function(className) {
|
||||
var arr = this._e.className.split(" ");
|
||||
var index = arr.indexOf(className)
|
||||
var toJoin = false;
|
||||
while (index !== -1) {
|
||||
arr.splice(index, 1);
|
||||
index = arr.indexOf(className)
|
||||
toJoin = true;
|
||||
}
|
||||
if (toJoin) {
|
||||
this._e.className = arr.join(" ");
|
||||
}
|
||||
this._e.classList.remove(className);
|
||||
},
|
||||
"_resetTheme": function() {
|
||||
this._onClearProperties();
|
||||
@ -191,9 +195,12 @@
|
||||
}
|
||||
if (needToTell) {
|
||||
this.trigger("changeLimits", this);
|
||||
if (this._w !== undefined && this._h !== undefined) {
|
||||
this.setSize(this._w, this._h);
|
||||
}
|
||||
}
|
||||
|
||||
return needToTell && this._events.changeLimits && this._events.changeLimits.length; //to see if someone actually going to listen that event
|
||||
return needToTell && this._events.changeLimits && this._events.changeLimits.length; //to see if someone actually going to listen that event, if not - return result
|
||||
},
|
||||
"setMaxSize": function(w, h) {
|
||||
this._o.maxWidth = w;
|
||||
@ -284,22 +291,29 @@
|
||||
View.ViewType = {
|
||||
Label: 0,
|
||||
|
||||
Image: 3,
|
||||
View: 4,
|
||||
Image: 4,
|
||||
Button: 5,
|
||||
View: 6,
|
||||
Enumeration: 7,
|
||||
|
||||
Page: 102,
|
||||
PanesList: 104
|
||||
PanesList: 104,
|
||||
Player: 107
|
||||
};
|
||||
|
||||
View.ReversedViewType = {
|
||||
"0": "Label",
|
||||
|
||||
"3": "Image",
|
||||
"4": "View",
|
||||
"4": "Image",
|
||||
"5": "Button",
|
||||
"6": "View",
|
||||
"7": "Enumeration",
|
||||
|
||||
"101": "Nav",
|
||||
"102": "Page",
|
||||
"104": "PanesList"
|
||||
"104": "PanesList",
|
||||
|
||||
"107": "Player"
|
||||
};
|
||||
|
||||
View.ViewTypesPaths = {
|
||||
@ -307,7 +321,10 @@
|
||||
Nav: "views/nav",
|
||||
Page: "views/page",
|
||||
PanesList: "views/panesList",
|
||||
Image: "views/image"
|
||||
Image: "views/image",
|
||||
Button: "views/button",
|
||||
Player: "views/player",
|
||||
Enumeration: "views/enumeration"
|
||||
};
|
||||
|
||||
View.constructors = {
|
||||
|
@ -3,6 +3,8 @@ var morgan = require("morgan");
|
||||
var favicon = require("serve-favicon");
|
||||
var Magnus = require("./core/magnus");
|
||||
|
||||
express.static.mime.types.wasm = 'application/wasm';
|
||||
|
||||
require("./lib/utils/globalMethods");
|
||||
|
||||
var config = require("./config");
|
||||
|
@ -5,7 +5,7 @@ var ENV = config.get('build');
|
||||
function getLogger(module) {
|
||||
var path = module.filename.split('/').slice(-2).join('/');
|
||||
|
||||
return new Winston.Logger({
|
||||
return Winston.createLogger({
|
||||
transports: [
|
||||
new Winston.transports.Console({
|
||||
colorize: true,
|
||||
|
@ -13,5 +13,6 @@ add_jslib(wModel/themeStorage.js lib/wModel/themeStorage ${MAGNUS_DIR} node)
|
||||
add_jslib(wModel/vocabulary.js lib/wModel/vocabulary ${MAGNUS_DIR} node)
|
||||
add_jslib(wModel/attributes.js lib/wModel/attributes ${MAGNUS_DIR} node)
|
||||
add_jslib(wModel/image.js lib/wModel/image ${MAGNUS_DIR} node)
|
||||
add_jslib(wModel/button.js lib/wModel/button ${MAGNUS_DIR} node)
|
||||
|
||||
add_subdirectory(proxy)
|
||||
|
@ -6,3 +6,4 @@ add_jslib(wModel/proxy/vocabulary.js lib/wModel/proxy/vocabulary ${MAGNUS_DIR} n
|
||||
add_jslib(wModel/proxy/catalogue.js lib/wModel/proxy/catalogue ${MAGNUS_DIR} node)
|
||||
|
||||
configure_file(pane.js pane.js)
|
||||
configure_file(panesList.js panesList.js)
|
||||
|
@ -4,12 +4,18 @@ var MVocabulary = require("./vocabulary");
|
||||
|
||||
var Address = require("../../wType/address");
|
||||
var Boolean = require("../../wType/boolean");
|
||||
var Uint64 = require("../../wType/uint64");
|
||||
var String = require("../../wType/string");
|
||||
var Vocabulary = require("../../wType/vocabulary");
|
||||
var Vector = require("../../wType/vector");
|
||||
|
||||
var Pane = MVocabulary.inherit({
|
||||
"className": "Pane",
|
||||
"constructor": function(address, controllerAddress, socket) {
|
||||
"constructor": function(address, controllerAddress, socket, actions) {
|
||||
MVocabulary.fn.constructor.call(this, address, controllerAddress, socket);
|
||||
|
||||
this._actions = new Vector();
|
||||
this._createActions(actions || []);
|
||||
if (this.constructor.pageAddress) {
|
||||
this.hasPageLink = true;
|
||||
|
||||
@ -17,14 +23,45 @@ var Pane = MVocabulary.inherit({
|
||||
this._pageLink = this.constructor.pageAddress["+"](new Address([id.toString()]));
|
||||
}
|
||||
},
|
||||
"destructor": function() {
|
||||
this._actions.destructor();
|
||||
if (this.hasPageLink) {
|
||||
this._pageLink.destructor();
|
||||
}
|
||||
|
||||
MVocabulary.fn.destructor.call(this);
|
||||
},
|
||||
"_createActions": function(actions) {
|
||||
for (var i = 0; i < actions.length; ++i) {
|
||||
var action = actions[i];
|
||||
var actionVC = new Vocabulary();
|
||||
actionVC.insert("type", new Uint64(action.type));
|
||||
var supported = false;
|
||||
|
||||
switch (action.type) {
|
||||
case 0: //this type of actions has only action itself, no additional arguments, what to do is desctibed in the view
|
||||
actionVC.insert("action", new String(action.action));
|
||||
supported = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (supported) {
|
||||
this._actions.push(actionVC);
|
||||
} else {
|
||||
actionVC.destructor();
|
||||
}
|
||||
}
|
||||
},
|
||||
"_getAllData": function() {
|
||||
var vc = this.controller.data.clone();
|
||||
|
||||
vc.insert("hasPageLink", new Boolean(this.hasPageLink));
|
||||
vc.insert("actions", this._actions.clone());
|
||||
if (this.hasPageLink) {
|
||||
vc.insert("pageLink", this._pageLink.clone());
|
||||
}
|
||||
|
||||
|
||||
return vc;
|
||||
}
|
||||
});
|
||||
|
66
magnus/lib/wModel/proxy/panesList.js
Normal file
66
magnus/lib/wModel/proxy/panesList.js
Normal file
@ -0,0 +1,66 @@
|
||||
"use strict";
|
||||
|
||||
var Vocabulary = require("../../wType/vocabulary");
|
||||
var Uint64 = require("../../wType/uint64");
|
||||
var String = require("../../wType/string");
|
||||
var Vector = require("../../wType/vector");
|
||||
|
||||
var Catalogue = require("./catalogue");
|
||||
|
||||
var PanesList = Catalogue.inherit({
|
||||
"className": "PanesList",
|
||||
"constructor": function(address, ctrlAddr, ctrlOpts, socket, actions) {
|
||||
Catalogue.fn.constructor.call(this, address, ctrlAddr, ctrlOpts, socket);
|
||||
|
||||
this._actions = new Vector();
|
||||
this._createActions(actions || []);
|
||||
},
|
||||
"destructor": function() {
|
||||
this._actions.destructor();
|
||||
|
||||
Catalogue.fn.destructor.call(this);
|
||||
},
|
||||
"_createActions": function(actions) {
|
||||
for (var i = 0; i < actions.length; ++i) {
|
||||
var action = actions[i];
|
||||
var actionVC = new Vocabulary();
|
||||
actionVC.insert("type", new Uint64(action.type));
|
||||
var supported = false;
|
||||
|
||||
switch (action.type) {
|
||||
case 0: //this type of actions has only action itself, no additional arguments, what to do is desctibed in the view
|
||||
actionVC.insert("action", new String(action.action));
|
||||
supported = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (supported) {
|
||||
this._actions.push(actionVC);
|
||||
} else {
|
||||
actionVC.destructor();
|
||||
}
|
||||
}
|
||||
},
|
||||
"_h_get": function(ev) {
|
||||
if (this.ready) {
|
||||
var vc = new Vocabulary();
|
||||
|
||||
vc.insert("data", this._getAllData());
|
||||
vc.insert("actions", this._actions.clone());
|
||||
this.response(vc, "get", ev);
|
||||
} else {
|
||||
this._waitingEvents.push(ev.clone());
|
||||
}
|
||||
},
|
||||
"_onRemoteData": function() {
|
||||
this.setReady(true);
|
||||
|
||||
var vc = new Vocabulary();
|
||||
vc.insert("data", this._getAllData());
|
||||
vc.insert("actions", this._actions.clone());
|
||||
|
||||
this.broadcast(vc, "get")
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = PanesList;
|
@ -2,7 +2,7 @@
|
||||
|
||||
var TempPage = require("./tempPage");
|
||||
var String = require("../lib/wModel/string");
|
||||
var ProxyCatModel = require("../lib/wModel/proxy/catalogue");
|
||||
var PanesList = require("../lib/wModel/proxy/panesList");
|
||||
var PaneModel = require("../lib/wModel/proxy/pane");
|
||||
var Image = require("../lib/wModel/image");
|
||||
var Model = require("../lib/wModel/model");
|
||||
@ -41,16 +41,23 @@ var AlbumPage = TempPage.inherit({
|
||||
var spacer = new Model(this._address["+"](new Address(["spacer"])));
|
||||
this.addItem(spacer, 0, 2, 2, 1);
|
||||
|
||||
this._songs = new ProxyCatModel(
|
||||
this._songs = new PanesList(
|
||||
this._address["+"](new Address(["songs"])),
|
||||
new Address(["songs"]),
|
||||
{
|
||||
sorting: {ascending: true, field: "name"},
|
||||
filter: {album: id.clone()}
|
||||
},
|
||||
proxySocket
|
||||
proxySocket,
|
||||
[{
|
||||
type: 0,
|
||||
action: "play"
|
||||
},
|
||||
{
|
||||
type: 0,
|
||||
action: "scheduledToPlay"
|
||||
}]
|
||||
);
|
||||
this._songs.className = "PanesList";
|
||||
var PaneClass = PaneModel.Songs;
|
||||
this._songs.setChildrenClass(PaneClass);
|
||||
this.addItem(this._songs, 2, 0, 1, 3, TempPage.Aligment.CenterTop);
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
var TempPage = require("./tempPage");
|
||||
var String = require("../lib/wModel/string");
|
||||
var ProxyCatModel = require("../lib/wModel/proxy/catalogue");
|
||||
var PanesList = require("../lib/wModel/proxy/panesList");
|
||||
var PaneModel = require("../lib/wModel/proxy/pane");
|
||||
|
||||
var VCController = require("../lib/wController/vocabulary");
|
||||
@ -22,7 +22,7 @@ var ArtistPage = TempPage.inherit({
|
||||
header.addProperty("fontFamily", "casualFont");
|
||||
this.addItem(header, 0, 0, 1, 1, TempPage.Aligment.CenterTop);
|
||||
|
||||
this._albums = new ProxyCatModel(
|
||||
this._albums = new PanesList(
|
||||
this._address["+"](new Address(["albums"])),
|
||||
new Address(["albums"]),
|
||||
{
|
||||
@ -36,16 +36,23 @@ var ArtistPage = TempPage.inherit({
|
||||
this._albums.setChildrenClass(PaneClass);
|
||||
this.addItem(this._albums, 1, 0, 1, 1, TempPage.Aligment.CenterTop);
|
||||
|
||||
this._songs = new ProxyCatModel(
|
||||
this._songs = new PanesList(
|
||||
this._address["+"](new Address(["songs"])),
|
||||
new Address(["songs"]),
|
||||
{
|
||||
sorting: {ascending: true, field: "name"},
|
||||
filter: {artist: id.clone()}
|
||||
},
|
||||
proxySocket
|
||||
proxySocket,
|
||||
[{
|
||||
type: 0,
|
||||
action: "play"
|
||||
},
|
||||
{
|
||||
type: 0,
|
||||
action: "scheduledToPlay"
|
||||
}]
|
||||
);
|
||||
this._songs.className = "PanesList";
|
||||
var PaneClass = PaneModel.Songs;
|
||||
this._songs.setChildrenClass(PaneClass);
|
||||
this.addItem(this._songs, 2, 0, 1, 1, TempPage.Aligment.CenterTop);
|
||||
|
@ -2,42 +2,45 @@
|
||||
|
||||
var Page = require("../lib/wModel/page");
|
||||
var String = require("../lib/wModel/string");
|
||||
var ProxyCatModel = require("../lib/wModel/proxy/catalogue");
|
||||
var PanesList = require("../lib/wModel/proxy/panesList");
|
||||
var PaneModel = require("../lib/wModel/proxy/pane");
|
||||
|
||||
var Address = require("../lib/wType/address");
|
||||
var Uint64 = require("../lib/wType/uint64");
|
||||
var Vocabulary = require("../lib/wType/vocabulary");
|
||||
|
||||
var Handler = require("../lib/wDispatcher/handler");
|
||||
|
||||
var List = Page.inherit({
|
||||
"className": "ListPage",
|
||||
"constructor": function(address, name, remoteAddress, socket, ChildClass) {
|
||||
"constructor": function(address, name, remoteAddress, socket, ChildClass, listOptions, paneActions) {
|
||||
Page.fn.constructor.call(this, address, name);
|
||||
|
||||
this._proxySocket = socket;
|
||||
this._ChildClass = ChildClass;
|
||||
this._listOptions = listOptions || new Vocabulary()
|
||||
|
||||
var header = new String(this._address["+"](new Address(["header"])), name);
|
||||
header.addProperty("fontFamily", "casualFont");
|
||||
this.addItem(header, 0, 0, 1, 1, Page.Aligment.CenterTop);
|
||||
|
||||
this._list = new ProxyCatModel(
|
||||
this._list = new PanesList(
|
||||
this._address["+"](new Address(["list"])),
|
||||
remoteAddress,
|
||||
{
|
||||
sorting: {ascending: true, field: "name"}
|
||||
},
|
||||
socket
|
||||
socket,
|
||||
paneActions
|
||||
);
|
||||
this._list.className = "PanesList";
|
||||
var PaneClass = PaneModel[name];
|
||||
if (!PaneClass) {
|
||||
PaneClass = PaneModel.inherit({});
|
||||
PaneClass.pageAddress = address;
|
||||
PaneModel[name] = PaneClass;
|
||||
}
|
||||
this._list.setChildrenClass(PaneClass);
|
||||
this.addItem(this._list, 1, 0, 1, 1, Page.Aligment.CenterTop);
|
||||
this.addItem(this._list, 1, 0, 1, 1, Page.Aligment.CenterTop, new Uint64(List.getModelTypeId(this._list)), listOptions);
|
||||
|
||||
this._reporterHandler = new Handler(this._address["+"](new Address(["subscribeMember"])), this, this._h_subscribeMember);
|
||||
},
|
||||
|
@ -7,6 +7,7 @@ var PanesList = require("../lib/wModel/panesList");
|
||||
var Address = require("../lib/wType/address");
|
||||
var Vocabulary = require("../lib/wType/vocabulary");
|
||||
var Boolean = require("../lib/wType/boolean");
|
||||
var Uint64 = require("../lib/wType/uint64");
|
||||
|
||||
var Link = require("../lib/wModel/link");
|
||||
|
||||
@ -135,12 +136,23 @@ var MusicPage = Page.inherit({
|
||||
this._albumsLink.label.addProperty("color", "primaryFontColor");
|
||||
this._albumsLink.addProperty("backgroundColor", "primaryColor");
|
||||
|
||||
var lOpts = new Vocabulary();
|
||||
lOpts.insert("nestWidth", new Uint64(300));
|
||||
this._songs = new List(
|
||||
this._addresses.songs.local.clone(),
|
||||
"Songs",
|
||||
this._addresses.songs.remote.clone(),
|
||||
socket,
|
||||
Song
|
||||
Song,
|
||||
lOpts,
|
||||
[{
|
||||
type: 0,
|
||||
action: "play"
|
||||
},
|
||||
{
|
||||
type: 0,
|
||||
action: "scheduledToPlay"
|
||||
}]
|
||||
);
|
||||
this._songsLink = new Link(this._address["+"](new Address(["songsLink"])), "Songs", this._addresses.songs.local.clone());
|
||||
this._songsLink.label.addProperty("fontSize", "largeFontSize");
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
var Page = require("../lib/wModel/page");
|
||||
var String = require("../lib/wModel/string");
|
||||
var Button = require("../lib/wModel/button");
|
||||
|
||||
var Address = require("../lib/wType/address");
|
||||
|
||||
@ -13,6 +14,16 @@ var TestPage = Page.inherit({
|
||||
var header = new String(this._address["+"](new Address(["message"])), "This is a test page");
|
||||
header.addProperty("fontFamily", "casualFont");
|
||||
this.addItem(header, 0, 0, 1, 1, Page.Aligment.CenterTop);
|
||||
|
||||
this._button = new Button(this._address["+"](new Address(["testButton"])));
|
||||
this._button.setLabel("Push me");
|
||||
this._button.on("activated", this._onActivate, this);
|
||||
this.addItem(this._button, 1, 0, 1, 1, Page.Aligment.CenterTop);
|
||||
},
|
||||
"_onActivate": function() {
|
||||
this.trigger("serviceMessage", "Button works!");
|
||||
this._button.setEnabled(false);
|
||||
setTimeout(this._button.setEnabled.bind(this._button, true), 3000);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -25,7 +25,7 @@ target_link_libraries(perturabo wSocket)
|
||||
target_link_libraries(perturabo wDispatcher)
|
||||
target_link_libraries(perturabo utils)
|
||||
target_link_libraries(perturabo wModel)
|
||||
target_link_libraries(perturabo wServerUtils)
|
||||
target_link_libraries(perturabo wDatabase)
|
||||
target_link_libraries(perturabo wServerUtils)
|
||||
|
||||
install(TARGETS perturabo RUNTIME DESTINATION bin)
|
||||
|
@ -86,11 +86,11 @@ void Perturabo::start()
|
||||
server->listen(8082);
|
||||
|
||||
cout << "Registering models..." << endl;
|
||||
attributes->registerModel(dispatcher, server);
|
||||
commands->registerModel(dispatcher, server);
|
||||
attributes->getRegistered(connector);
|
||||
commands->getRegistered(connector);
|
||||
|
||||
for (; beg != end; ++beg) {
|
||||
beg->second->registerModel(dispatcher, server);
|
||||
beg->second->getRegistered(connector);
|
||||
}
|
||||
|
||||
cout << "Opening and indexing databases..." << endl;
|
||||
@ -115,11 +115,11 @@ void Perturabo::stop()
|
||||
commands->enableCommand(W::String(u"clearDatabase"), false);
|
||||
|
||||
for (; beg != end; ++beg) {
|
||||
beg->second->unregisterModel();
|
||||
beg->second->getUnregistered();
|
||||
}
|
||||
|
||||
commands->unregisterModel();
|
||||
attributes->unregisterModel();
|
||||
commands->getUnregistered();
|
||||
attributes->getUnregistered();
|
||||
server->stop();
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 2.8.12)
|
||||
project(test)
|
||||
|
||||
find_package(CxxTest)
|
||||
cmake_policy(SET CMP0071 NEW)
|
||||
if(CXXTEST_FOUND)
|
||||
include_directories(${CXXTEST_INCLUDE_DIR})
|
||||
enable_testing()
|
||||
|
Loading…
Reference in New Issue
Block a user