Extensions for JSEvents

This commit is contained in:
Peter Schojer 2008-05-14 12:31:14 +00:00
parent 0d3e23167a
commit fba8e67818
11 changed files with 212 additions and 58 deletions

View File

@ -43,7 +43,10 @@
#include "Poco/WebWidgets/ExtJS/ExtJS.h"
#include "Poco/WebWidgets/Form.h"
#include "Poco/WebWidgets/LookAndFeel.h"
#include "Poco/WebWidgets/JavaScriptEvent.h"
#include <ostream>
#include <set>
#include <map>
namespace Poco {
@ -97,7 +100,20 @@ public:
static std::string convertPocoDateToPHPDate(const std::string& dateTimeFmt);
/// Converts a poco date time format string to its PHP/extjs equivalent
static bool writeJSEvent(std::ostream& out, const std::string& eventName, const std::set<JSDelegate>& delegates);
/// writes all JS Delegates for a single named JSEvent.
/// Returns true if data was written, false if no delegates were present and no event handler was written.
static bool writeJSEventPlusServerCallback(std::ostream& out, const std::string& eventName, const std::set<JSDelegate>& delegates, const std::map<std::string, std::string>& addServerParams);
/// writes all JS Delegates for a single named JSEvent. adds a callback to the server
/// Returns true if data was written, false if no delegates were present and no event handler was written.
/// an addParam that should be treated as a variable must start with the '+' character!
private:
static void writeCodeForDelegate(std::ostream& invoke, std::ostream& jsOut, const std::set<JSDelegate>& jsDels);
static void writeCodeForDelegate(std::ostream& invoke, std::ostream& jsOut, const JSDelegate& jsDel, int cnt);
static std::string createFunctionCode(const std::string& eventName, const std::map<std::string, std::string>& addParams);
static void convertPocoDateToPHPDate(char in, std::string& result);
static void convertPHPDateToPocoDate(char in, std::string& result);
static void escapeCharForPHP(char in, std::string& result);

View File

@ -80,31 +80,24 @@ void TableRenderer::renderBody(const Renderable* pRenderable, const RenderContex
void TableRenderer::renderProperties(const Table* pTable, const RenderContext& context, std::ostream& ostr)
{
Utility::writeRenderableProperties(pTable, ostr);
WebApplication& app = WebApplication::instance();
Renderable::ID id = app.getCurrentPage()->id();
// add an afterEdit handler
// event handlers can be defined in the constructor after the listeners member
//{
// 'afterEdit' : {
// fn: function(obj){obj.row, obj.column, obj.value, tableId}
//},
std::ostringstream uri;
uri << "'" <<app.getURI().toString();
uri << ";"; //mark as AJAX request
uri << RequestHandler::KEY_ID << "=" << id << "&";
uri << Table::FIELD_COL << "='+obj.column+'&";
uri << Table::FIELD_ROW << "='+obj.row+'&" << Table::FIELD_VAL << "='+obj.value";
//obj.commitChanges... hides the red triangle
ostr << ", listeners: {'afteredit':{fn:function(obj){var uri=" << uri.str() << ";obj.grid.getStore().commitChanges();";
ostr << "Ext.Ajax.request({url:uri,";
ostr << "failure:function(){Ext.MessageBox.alert('Status','Failed to write changes back to server.');}});}}},";
Utility::writeRenderableProperties(pTable, ostr);
static const std::string afterEdit("afterEdit");
std::map<std::string, std::string> addParams;
addParams.insert(std::make_pair(Table::FIELD_COL, "+obj.column"));
addParams.insert(std::make_pair(Table::FIELD_ROW, "+obj.row"));
addParams.insert(std::make_pair(Table::FIELD_VAL, "+obj.value"));
JavaScriptEvent<int> ev;
ev.add(jsDelegate("function(obj){obj.grid.getStore().commitChanges();}"));
ostr << ",listeners:{";
Utility::writeJSEventPlusServerCallback(ostr, afterEdit, ev.jsDelegates(), addParams);
ostr << "},";
renderColumns(pTable, context, ostr);
ostr << ",clicksToEdit:1,store:";
renderStore(pTable, ostr);
app.registerAjaxProcessor(Poco::NumberFormatter::format(id), const_cast<Table*>(pTable));
WebApplication::instance().registerAjaxProcessor(Poco::NumberFormatter::format(pTable), const_cast<Table*>(pTable));
}

View File

@ -88,10 +88,12 @@
#include "Poco/WebWidgets/TabView.h"
#include "Poco/WebWidgets/ListBoxCell.h"
#include "Poco/WebWidgets/Table.h"
#include "Poco/WebWidgets/WebApplication.h"
#include "Poco/WebWidgets/RequestHandler.h"
#include "Poco/String.h"
#include "Poco/DateTimeFormat.h"
#include <sstream>
namespace Poco {
@ -379,4 +381,129 @@ Form::Ptr Utility::insideForm(const Cell* pChild)
}
bool Utility::writeJSEvent(std::ostream& out, const std::string& eventName, const std::set<JSDelegate>& delegates)
{
//'click' : {
// fn: this.onClick,
// scope: this,
// delay: 100
//}
if (delegates.empty())
return false;
out << "'" << eventName << "':";
if (delegates.size() == 1)
out << "{fn:" << delegates.begin()->jsCode() << "}";
else
{
// rather simple way to support more than one delegate
// TODO: there sure is a more efficient way to do this
std::ostringstream invoke;
invoke << "invoke:function(" << JS_EVENTARGNAME << "){";
out << "{fn:function(" << JS_EVENTARGNAME << "){var all={";
writeCodeForDelegate(invoke, out, delegates);
invoke << "}";
out << invoke.str() << "};"; //closes all
out << "all.invoke(" << JS_EVENTARGNAME << ");";
out << "}"; //closes fn
out << "}"; //closes function
}
return true;
}
void Utility::writeCodeForDelegate(std::ostream& invoke, std::ostream& out, const std::set<JSDelegate>& jsDels)
{
int cnt(0);
std::set<JSDelegate>::const_iterator it = jsDels.begin();
for (; it != jsDels.end(); ++it)
{
writeCodeForDelegate(invoke, out, *it, cnt);
++cnt;
}
}
void Utility::writeCodeForDelegate(std::ostream& invoke, std::ostream& out, const JSDelegate& jsDel, int cnt)
{
std::string code(Poco::trim(jsDel.jsCode()));
if (code.find("function") == 0)
{
// inline function definition
out << "d" << cnt << ":" << code << ",";
invoke << "this.d" << cnt << "(" << JS_EVENTARGNAME << ");";
}
else
{
out << "d" << cnt << ":function(" << JS_EVENTARGNAME << "){" << code;
if (!code.empty() && code[code.size()-1] != ';')
out << ";";
out << "},";
invoke << "this.d" << cnt << "(" << JS_EVENTARGNAME << ");";
}
}
bool Utility::writeJSEventPlusServerCallback(std::ostream& out, const std::string& eventName, const std::set<JSDelegate>& delegates, const std::map<std::string, std::string>& addServerParams)
{
// rather simple way to support more than one delegate
// TODO: there sure is a more efficient way to do this
std::ostringstream invoke;
invoke << "invoke:function(" << JS_EVENTARGNAME << "){";
out << "{fn:function(" << JS_EVENTARGNAME << "){var all={";
writeCodeForDelegate(invoke, out, delegates);
//write server callback
writeCodeForDelegate(invoke, out, jsDelegate(createFunctionCode(eventName, addServerParams)), static_cast<int>(delegates.size()));
invoke << "}";
out << invoke.str() << "};"; //closes all
out << "all.invoke(" << JS_EVENTARGNAME << ");";
out << "}"; //closes fn
out << "}"; //closes function
return true;
}
std::string Utility::createFunctionCode(const std::string& eventName, const std::map<std::string, std::string>& addParams)
{
WebApplication& app = WebApplication::instance();
Renderable::ID id = app.getCurrentPage()->id();
std::ostringstream uri;
uri << "'" << app.getURI().toString();
uri << ";"; //mark as AJAX request
uri << RequestHandler::KEY_ID << "=" << id << "&";
uri << RequestHandler::KEY_EVID << "=" << eventName;
// add optional params
std::map<std::string, std::string>::const_iterator it = addParams.begin();
for (; it != addParams.end(); ++it)
{
uri << "&" << it->first;
if (it->second.empty())
{
}
else
{
if (it->second[0] == '+')
{
// a variable was added
uri << "='" << it->second << "+'";
}
else
uri << "=" << it->second;
}
}
// now add the callback code
std::ostringstream function;
function << "function(" << JS_EVENTARGNAME << "){var uri=" << uri.str() << ";";
function << "Ext.Ajax.request({url:uri,";
function << "failure:function(){Ext.MessageBox.alert('Status','Failed to write changes back to server.');}});}";
return function.str();
}
} } } // namespace Poco::WebWidgets::ExtJS

View File

@ -67,6 +67,7 @@
#include "Poco/WebWidgets/ImageButtonCell.h"
#include "Poco/WebWidgets/TextEditCell.h"
#include "Poco/WebWidgets/SimpleTableModel.h"
#include "Poco/WebWidgets/JSDelegate.h"
#include "Poco/TeeStream.h"
#include "Poco/DateTimeFormat.h"
#include "Poco/DateTime.h"
@ -1315,6 +1316,36 @@ void ExtJSTest::testTableImageButton()
}
void ExtJSTest::testJSEvent()
{
Button::Ptr pBut(new Button());
pBut->buttonClicked.add(jsDelegate("someFunction(obj)"));
pBut->buttonClicked.add(jsDelegate("function(obj){alert('Click');}"));
std::ostringstream out;
Utility::writeJSEvent(out, "clicked", pBut->buttonClicked.jsDelegates());
std::string result(out.str());
static const std::string expected("'clicked':"
"{"
"fn:function(obj){"
"var all={"
"d0:function(obj){"
"alert('Click');"
"},"
"d1:function(obj){"
"someFunction(obj);"
"},"
"invoke:function(obj){"
"this.d0(obj);"
"this.d1(obj);"
"}"
"};"
"all.invoke(obj);"
"}"
"}");
assert (result == expected);
}
void ExtJSTest::setUp()
{
}
@ -1365,6 +1396,7 @@ CppUnit::Test* ExtJSTest::suite()
CppUnit_addTest(pSuite, ExtJSTest, testTableComboBox);
CppUnit_addTest(pSuite, ExtJSTest, testTableButton);
CppUnit_addTest(pSuite, ExtJSTest, testTableImageButton);
CppUnit_addTest(pSuite, ExtJSTest, testJSEvent);
return pSuite;
}

View File

@ -81,7 +81,7 @@ public:
void testTableComboBox();
void testTableButton();
void testTableImageButton();
void testJSEvent();
void setUp();
void tearDown();

View File

@ -42,7 +42,7 @@
#include "Poco/WebWidgets/Control.h"
#include "Poco/WebWidgets/Event.h"
#include "Poco/BasicEvent.h"
#include "Poco/WebWidgets/JavaScriptEvent.h"
namespace Poco {
@ -59,7 +59,7 @@ public:
typedef Poco::AutoPtr<Button> Ptr;
typedef Event<Button> ButtonEvent;
Poco::BasicEvent<ButtonEvent> buttonClicked;
JavaScriptEvent<ButtonEvent> buttonClicked;
Button(const std::string& name);
/// Creates a Button with the given name.

View File

@ -51,46 +51,34 @@ class WebWidgets_API JSDelegate
/// A JSDelegate adds a javascript method call to a JavaScriptEvent
{
public:
JSDelegate(const std::string& fct, const std::string& file);
JSDelegate(const std::string& jsCode);
/// Creates the JSDelegate.
virtual ~JSDelegate();
/// Destroys the JSDelegate.
const std::string& functionName() const;
/// The JavaScript function to call. It is assumed that the
/// function takes one parameter, which is of type JavaScript EventArgs
const std::string& jsFile() const;
/// The javascript file we need to import so that
/// the function is available
const std::string& jsCode() const;
/// The javascript code that should be executed
bool operator<(const JSDelegate& del) const;
private:
std::string _functionName;
std::string _jsFile;
std::string _jsCode;
};
inline const std::string& JSDelegate::functionName() const
inline const std::string& JSDelegate::jsCode() const
{
return _functionName;
return _jsCode;
}
inline const std::string& JSDelegate::jsFile() const
static JSDelegate jsDelegate(const std::string& jsCode);
inline JSDelegate jsDelegate(const std::string& jsCode)
{
return _jsFile;
}
static JSDelegate jsDelegate(const std::string& fct, const std::string& file);
inline JSDelegate jsDelegate(const std::string& fct, const std::string& file)
{
return JSDelegate(fct, file);
return JSDelegate(jsCode);
}

View File

@ -51,8 +51,11 @@ namespace Poco {
namespace WebWidgets {
static const std::string JS_EVENTARGNAME("obj"); /// each javascript method receives one single param which is named JS_EVENTARGNAME
template <class TArgs>
class WebWidgets_API JavaScriptEvent: public Poco::AbstractEvent <
class JavaScriptEvent: public Poco::AbstractEvent <
TArgs, Poco::DefaultStrategy<TArgs, Poco::AbstractDelegate<TArgs>, Poco::p_less<Poco::AbstractDelegate<TArgs> > >,
Poco::AbstractDelegate<TArgs>
>

View File

@ -57,7 +57,8 @@ class WebWidgets_API RequestHandler: public Poco::Net::HTTPRequestHandler
/// The HTTP request handler for WebWidgets.
{
public:
static const std::string KEY_ID;
static const std::string KEY_ID; /// key for form param contains id
static const std::string KEY_EVID; /// form param containing the event name
RequestHandler(WebApplication& app);
/// Creates the RequestHandler, using the given WebApplication.

View File

@ -41,9 +41,8 @@ namespace Poco {
namespace WebWidgets {
JSDelegate::JSDelegate(const std::string& fct, const std::string& file):
_functionName(fct),
_jsFile(file)
JSDelegate::JSDelegate(const std::string& code):
_jsCode(code)
{
}
@ -55,13 +54,7 @@ JSDelegate::~JSDelegate()
bool JSDelegate::operator<(const JSDelegate& del) const
{
if (_functionName < del._functionName)
return true;
if (_functionName > del._functionName)
return false;
return _jsFile < del._jsFile;
return (_jsCode < del._jsCode);
}

View File

@ -55,6 +55,7 @@ namespace WebWidgets {
const std::string RequestHandler::KEY_ID("id");
const std::string RequestHandler::KEY_EVID("evId");
RequestHandler::RequestHandler(WebApplication& app):