changed signature of on* event handlers: 1. event, 2. widget (context is DOM node), event handlers have now type "js" in attribute description and get automatic converted to a function, no more need to call et2_compileLegacyJS

This commit is contained in:
Ralf Becker 2013-10-09 14:35:03 +00:00
parent 71c2a554bd
commit 0163442f37
15 changed files with 133 additions and 127 deletions

View File

@ -17,10 +17,6 @@
app.admin = AppJS.extend(
{
appname: 'admin',
/**
* et2 widget container
*/
et2: null,
/**
* reference to splitter
*/
@ -47,7 +43,6 @@ app.admin = AppJS.extend(
*/
destroy: function()
{
delete this.et2;
// call parent
this._super.apply(this, arguments);
},
@ -64,8 +59,6 @@ app.admin = AppJS.extend(
// call parent
this._super.apply(this, arguments);
this.et2 = _et2.widgetContainer;
var iframe = this.iframe = this.et2.getWidgetById('iframe');
if (iframe)
{

View File

@ -43,7 +43,7 @@
</grid>
</template>
<template id="admin.index" template="" lang="" group="0" version="1.9.001">
<tree autoloading="admin_ui::ajax_tree" id="tree" onclick="app.admin.run(widget.event_args[0],widget);" parent_node="admin_tree_target" std_images="orange-ball"/>
<tree autoloading="admin_ui::ajax_tree" id="tree" onclick="app.admin.run" parent_node="admin_tree_target" std_images="orange-ball"/>
<description id="msg" class="message"/>
<split dock_side="topDock" id="splitter" orientation="h">
<nextmatch id="nm" template="admin.index.rows"/>

View File

@ -42,7 +42,7 @@ var et2_baseWidget = et2_DOMWidget.extend(et2_IAligned,
},
"onclick": {
"name": "onclick",
"type": "string",
"type": "js",
"description": "JS code which is executed when the element is clicked."
}
},
@ -233,19 +233,20 @@ var et2_baseWidget = et2_DOMWidget.extend(et2_IAligned,
return this.getDOMNode(this);
},
click: function(_node) {
if (this.onclick)
/**
* Click handler calling custom handler set via onclick attribute to this.onclick
*
* @param _ev
* @returns
*/
click: function(_ev) {
if(typeof this.onclick == 'function')
{
if(typeof this.onclick == 'function')
{
// Make sure function gets a reference to the widget
var args = Array.prototype.slice.call(arguments);
if(args.indexOf(this) == -1) args.push(this);
return this.onclick.apply(this, args);
} else {
return (et2_compileLegacyJS(this.options.onclick, this, _node))();
}
// Make sure function gets a reference to the widget, splice it in as 2. argument if not
var args = Array.prototype.slice.call(arguments);
if(args.indexOf(this) == -1) args.splice(1, 0, this);
return this.onclick.apply(this, args);
}
return true;

View File

@ -84,8 +84,13 @@ function et2_form_name(_cname,_name)
* Checks whether the given value is of the given type. Strings are converted
* into the corresponding type. The (converted) value is returned. All supported
* types are listed in the et2_validTypes array.
*
* @param mixed _val value
* @param string _type a valid type eg. "string" or "js"
* @param string _attr attribute name
* @param object _widget
*/
function et2_checkType(_val, _type, _attr, _cname)
function et2_checkType(_val, _type, _attr, _widget)
{
if (typeof _attr == "undefined")
{
@ -241,10 +246,15 @@ function et2_checkType(_val, _type, _attr, _cname)
}
}
}
if (typeof _val == "string")
{
return _val; // get compiled later in widgets own initAttributes, as widget is not yet initialised
}
}
// We should never come here
throw("Invalid type identifier '" + _attr + ": " + _type + "' supplied by '" + _cname + "'");
throw("Invalid type identifier '" + _attr + ": " + _type);
}
/**

View File

@ -403,7 +403,7 @@
}
var val = et2_checkType(_value, this.attributes[_name].type,
_name);
_name, this);
if (typeof this["set_" + _name] == "function")
{
@ -438,7 +438,7 @@
if (!this.attributes[key].ignore)
{
_attrs[key] = et2_checkType(_attrs[key], this.attributes[key].type,
key);
key, this);
}
}
else

View File

@ -36,12 +36,12 @@ var et2_inputWidget = et2_valueWidget.extend([et2_IInput,et2_ISubmitListener],
},
"onchange": {
"name": "onchange",
"type": "string",
"type": "js",
"description": "JS code which is executed when the value changes."
},
"onfocus": {
"name": "onfocus",
"type": "string",
"type": "js",
"description": "JS code which get executed when wiget receives focus."
},
"validation_error": {
@ -150,20 +150,13 @@ var et2_inputWidget = et2_valueWidget.extend([et2_IInput,et2_ISubmitListener],
focus: function(_node)
{
if (this.options.onfocus)
if(typeof this.options.onfocus == 'function')
{
if(typeof this.options.onfocus == 'function')
{
// Make sure function gets a reference to the widget
var args = Array.prototype.slice.call(arguments);
if(args.indexOf(this) == -1) args.push(this);
// Make sure function gets a reference to the widget
var args = Array.prototype.slice.call(arguments);
if(args.indexOf(this) == -1) args.push(this);
return this.options.onfocus.apply(this, args);
}
else
{
return (et2_compileLegacyJS(this.options.onfocus, this, _node))();
}
return this.options.onfocus.apply(this, args);
}
},

View File

@ -129,9 +129,9 @@
// Code is app.appname.function, add the arguments so it can be executed
if (typeof _code == 'string' && _code.indexOf('app') == 0 && _code.split('.').length >= 3 && _code.indexOf('(') == -1)
{
_code += '(egw,widget,window,document)';
_code += '(ev,widget)';
}
var func = new Function('egw', 'widget', 'window', 'document', _code);
var func = new Function('ev', 'widget', _code);
} catch(e) {
_widget.egw().debug('error', 'Error while compiling JS code ', _code);
return (function() {return false;});
@ -139,16 +139,12 @@
// Execute the code and return its results, pass the egw instance and
// the widget
return function() {
// Get the egw reference
var egw = _widget.egw();
return function(ev) {
// Dump the executed code for debugging
egw.debug('log', 'Executing legacy JS code: ', _code);
// Return the result of the called function
return func.call(context, egw, _widget, egw.window,
egw.window.document);
return func.call(context, ev, _widget);
};
};

View File

@ -752,6 +752,32 @@ var et2_widget = Class.extend(
}
}
},
/**
* The initAttributes function sets the attributes to their default
* values. The attributes are not overwritten, which means, that the
* default is only set, if either a setter exists or this[propName] does
* not exist yet.
*
* Overwritten here to compile legacy JS code in attributes of type "js"
*/
initAttributes: function(_attrs) {
for (var key in _attrs)
{
if (typeof this.attributes[key] != "undefined" && !this.attributes[key].ignore && !(_attrs[key] == undefined))
{
var val = _attrs[key];
// compile string values of attribute type "js" to functions
if (this.attributes[key].type == 'js' && typeof _attrs[key] == 'string')
{
val = et2_compileLegacyJS(val, this,
this.instanceOf(et2_inputWidget) ? this.getInputNode() :
(this.implements(et2_IDOMNode) ? this.getDOMNode() : null));
}
this.setAttribute(key, val, false);
}
}
},
doLoadingFinished: function() {
return true;

View File

@ -94,7 +94,7 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput],
},
"onselect": {
"name": "onselect",
"type": "string",
"type": "js",
"description": "JS code which gets executed when rows are selected. Can also be a app.appname.func(selected) style method"
},
"onfiledrop": {
@ -485,26 +485,9 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput],
*/
onselect: function(action,senders) {
// Execute the JS code connected to the event handler
if (this.options.onselect)
if (typeof this.options.onselect == 'function')
{
if (typeof this.options.onselect == "string" &&
this.options.onselect.substr(0,4) == "app." && window.app)
{
var parts = this.options.onselect.split(".");
if(parts.length == 3 && typeof window.app[parts[1]] == "object" &&
typeof window.app[parts[1]][parts[2]] == "function")
{
// Call as Action callback
//window.app[parts[1]][parts[2]].apply( window.app[parts[1]], arguments);
window.app[parts[1]][parts[2]].apply( window.app[parts[1]], [this,this.getSelection().ids]);
}
}
// Exectute the legacy JS code
else if (!(et2_compileLegacyJS(this.options.onselect, this, this.div))())
{
return false;
}
return this.options.onselect.call(this, this.getSelection().ids, this);
}
},
@ -1949,11 +1932,6 @@ var et2_nextmatch_header = et2_baseWidget.extend(et2_INextmatchHeader,
"type": "string",
"description": "Caption for the nextmatch header",
"translate": true
},
"onchange": {
"name": "onchange",
"type": "string",
"description": "JS code which is executed when the value changes."
}
},

View File

@ -43,7 +43,7 @@ var et2_button = et2_baseWidget.extend([et2_IInput, et2_IDetachedDOM],
},
"onclick": {
"name": "onclick",
"type": "string",
"type": "js",
"description": "JS code which gets executed when the button is clicked"
},
"accesskey": {
@ -177,18 +177,19 @@ var et2_button = et2_baseWidget.extend([et2_IInput, et2_IDetachedDOM],
return this.btn ? this.btn[0] : (this.image ? this.image[0] : null);
},
onclick: function(_node) {
/**
* Overwritten to maintain an internal clicked attribute
*
* @param _ev
* @returns {Boolean}
*/
click: function(_ev) {
this.clicked = true;
// Execute the JS code connected to the event handler
if (this.options.onclick)
if (!this._super.apply(this, arguments))
{
// Exectute the legacy JS code
if (!(et2_compileLegacyJS(this.options.onclick, this, _node))())
{
this.clicked = false;
return false;
}
this.clicked = false;
return false;
}
// Submit the form
@ -197,6 +198,7 @@ var et2_button = et2_baseWidget.extend([et2_IInput, et2_IDetachedDOM],
this.getInstanceManager().submit(this); //TODO: this only needs to be passed if it's in a datagrid
}
this.clicked = false;
return true;
},
set_label: function(_value) {

View File

@ -60,7 +60,7 @@ var et2_dropdown_button = et2_inputWidget.extend(
},
"onclick": {
"name": "onclick",
"type": "string",
"type": "js",
"description": "JS code which gets executed when the button is clicked"
},
"select_options": {
@ -272,21 +272,28 @@ var et2_dropdown_button = et2_inputWidget.extend(
}
},
onclick: function(_node) {
/**
* Overwritten to maintain an internal clicked attribute
*
* @param _ev
* @returns {Boolean}
*/
click: function(_ev) {
this.clicked = true;
// Execute the JS code connected to the event handler
if (this.options.onclick)
if (!this._super.apply(this, arguments))
{
// Exectute the legacy JS code
if (!(et2_compileLegacyJS(this.options.onclick, this, _node))())
{
this.clicked = false;
return false;
}
this.clicked = false;
return false;
}
// Submit the form
if (this._type != "buttononly")
{
this.getInstanceManager().submit(this); //TODO: this only needs to be passed if it's in a datagrid
}
this.clicked = false;
return true;
},
onselect: function(event, selected_node) {

View File

@ -47,11 +47,6 @@ var et2_progress = et2_valueWidget.extend([et2_IDetachedDOM],
"type": "string",
"description": "The label is displayed as the title. The label can contain variables, as descript for name. If the label starts with a '@' it is replaced by the value of the content-array at this index (with the '@'-removed and after expanding the variables).",
"translate": true
},
"onchange": {
"name": "onchange",
"type": "string",
"description": "JS code which is executed when the value changes."
}
},
legacyOptions: ["href", "extra_link_target", "imagemap", "extra_link_popup", "id"],

View File

@ -48,20 +48,17 @@ var et2_tree = et2_inputWidget.extend(
},
"onclick": {
"name": "onClick",
"type": "string",
"default": "",
"type": "js",
"description": "JS code which gets executed when clicks on text of a node"
},
"onselect": {
"name": "onSelect",
"type": "string",
"default": "",
"type": "js",
"description": "Javascript executed when user selects a node"
},
"oncheck": {
"name": "onCheck",
"type": "string",
"default": "",
"type": "js",
"description": "Javascript executed when user checks a node"
},
// onChange event is mapped depending on multiple to onCheck or onSelect
@ -167,7 +164,7 @@ var et2_tree = et2_inputWidget.extend(
},
// overwrite default onclick to do nothing, as we install onclick via dhtmlxtree
onclick: function(_node) {},
click: function(_node) {},
createTree: function(widget) {
widget.input = new dhtmlXTreeObject({
@ -184,26 +181,6 @@ var et2_tree = et2_inputWidget.extend(
// Add in the callback so we can keep the two in sync
widget.input.AJAX_callback = function() { widget._dhtmlxtree_json_callback(JSON.parse(this.response), widget.input.lastLoadedXMLId);};
// attach all event handlers (attributs starting with "on"), if they are set
for(var name in widget.options)
{
if (name.substr(0,2) == 'on' && widget.options[name])
{
// automatic convert onChange event to oncheck or onSelect depending on multiple is used or not
if (name == 'onchange') name = widget.options.multiple ? 'oncheck' : 'onselect';
widget.input.attachEvent(widget.attributes[name].name, function(_args){
var _widget = widget; // closure to pass in et2 widget (1. param of event handler)
// use widget attributes to pass arguments and name of event to handler
_widget.event_args = arguments;
_widget.event_name = this.callEvent.arguments[0].substr(3);
var _js = _widget.options[_widget.event_name] || _widget.options.onchange;
(et2_compileLegacyJS(_js, _widget, this))();
delete _widget.event_args;
delete _widget.event_name;
});
}
}
if (widget.options.autoloading)
{
var url = widget.options.autoloading;
@ -217,6 +194,34 @@ var et2_tree = et2_inputWidget.extend(
widget.input.setDataMode('JSON');
}
},
/**
* Install event handlers on tree
*
* @param _name
* @param _handler
*/
_install_handler: function(_name, _handler)
{
if (typeof _handler == 'function')
{
if(this.input == null) this.createTree(this);
// automatic convert onChange event to oncheck or onSelect depending on multiple is used or not
if (_name == 'onchange') _name = this.options.multiple ? 'oncheck' : 'onselect';
var handler = _handler;
var widget = this;
this.input.attachEvent(_name, function(_id){
var args = jQuery.makeArray(arguments);
// splice in widget as 2. parameter, 1. is new node-id, now 3. is old node id
args.splice(1, 0, widget);
handler.apply(this, args);
});
}
},
set_onchange: function(_handler) { this._install_handler('onchange', _handler); },
set_onclick: function(_handler) { this._install_handler('onclick', _handler); },
set_onselect: function(_handler) { this._install_handler('onselect', _handler); },
set_select_options: function(options)
{

View File

@ -39,7 +39,7 @@ app.mail = AppJS.extend(
/**
* Initialize javascript for this application
*
* @memberOf app.mail
* @memberOf mail
*/
init: function() {
this._super.apply(this,arguments);
@ -428,7 +428,7 @@ app.mail = AppJS.extend(
* @param nextmatch et2_nextmatch The widget whose row was selected
* @param selected Array Selected row IDs. May be empty if user unselected all rows.
*/
mail_preview: function(nextmatch, selected) {
mail_preview: function(selected, nextmatch) {
//console.log("mail_preview",nextmatch, selected);
// Empty values, just in case selected is empty (user cleared selection)
var dataElem = {data:{subject:"",fromaddress:"",toaddress:"",date:"",subject:""}};

View File

@ -38,7 +38,7 @@
</grid>
</template>
<template id="mail.index" template="" lang="" group="0" version="1.9.001">
<tree autoloading="mail.mail_ui.ajax_foldertree" id="nm[foldertree]" onclick="app.mail.mail_changeFolder(widget.event_args[0],widget);" parent_node="mail-tree_target"/>
<tree autoloading="mail.mail_ui.ajax_foldertree" id="nm[foldertree]" onclick="app.mail.mail_changeFolder" parent_node="mail-tree_target"/>
<html id="msg"/>
<buttononly id="button[mailcreate]" onclick="app.mail.mail_compose(false);" label="Compose" parent_node="mail-index_buttonmailcreate"/>
<buttononly id="button[testhtmlarea]" onclick="app.mail.mail_testhtmlarea(false);" label="Test HTML Area" parent_node="mail-index_buttontesthtmlarea"/>