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

View File

@ -43,7 +43,7 @@
</grid> </grid>
</template> </template>
<template id="admin.index" template="" lang="" group="0" version="1.9.001"> <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"/> <description id="msg" class="message"/>
<split dock_side="topDock" id="splitter" orientation="h"> <split dock_side="topDock" id="splitter" orientation="h">
<nextmatch id="nm" template="admin.index.rows"/> <nextmatch id="nm" template="admin.index.rows"/>

View File

@ -42,7 +42,7 @@ var et2_baseWidget = et2_DOMWidget.extend(et2_IAligned,
}, },
"onclick": { "onclick": {
"name": "onclick", "name": "onclick",
"type": "string", "type": "js",
"description": "JS code which is executed when the element is clicked." "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); 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, splice it in as 2. argument if not
{ var args = Array.prototype.slice.call(arguments);
// Make sure function gets a reference to the widget if(args.indexOf(this) == -1) args.splice(1, 0, this);
var args = Array.prototype.slice.call(arguments);
if(args.indexOf(this) == -1) args.push(this); return this.onclick.apply(this, args);
return this.onclick.apply(this, args);
} else {
return (et2_compileLegacyJS(this.options.onclick, this, _node))();
}
} }
return true; 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 * Checks whether the given value is of the given type. Strings are converted
* into the corresponding type. The (converted) value is returned. All supported * into the corresponding type. The (converted) value is returned. All supported
* types are listed in the et2_validTypes array. * 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") 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 // 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, var val = et2_checkType(_value, this.attributes[_name].type,
_name); _name, this);
if (typeof this["set_" + _name] == "function") if (typeof this["set_" + _name] == "function")
{ {
@ -438,7 +438,7 @@
if (!this.attributes[key].ignore) if (!this.attributes[key].ignore)
{ {
_attrs[key] = et2_checkType(_attrs[key], this.attributes[key].type, _attrs[key] = et2_checkType(_attrs[key], this.attributes[key].type,
key); key, this);
} }
} }
else else

View File

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

View File

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

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() { doLoadingFinished: function() {
return true; return true;

View File

@ -94,7 +94,7 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput],
}, },
"onselect": { "onselect": {
"name": "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" "description": "JS code which gets executed when rows are selected. Can also be a app.appname.func(selected) style method"
}, },
"onfiledrop": { "onfiledrop": {
@ -485,26 +485,9 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput],
*/ */
onselect: function(action,senders) { onselect: function(action,senders) {
// Execute the JS code connected to the event handler // 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" && return this.options.onselect.call(this, this.getSelection().ids, this);
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;
}
} }
}, },
@ -1949,11 +1932,6 @@ var et2_nextmatch_header = et2_baseWidget.extend(et2_INextmatchHeader,
"type": "string", "type": "string",
"description": "Caption for the nextmatch header", "description": "Caption for the nextmatch header",
"translate": true "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": { "onclick": {
"name": "onclick", "name": "onclick",
"type": "string", "type": "js",
"description": "JS code which gets executed when the button is clicked" "description": "JS code which gets executed when the button is clicked"
}, },
"accesskey": { "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); 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; this.clicked = true;
// Execute the JS code connected to the event handler if (!this._super.apply(this, arguments))
if (this.options.onclick)
{ {
// Exectute the legacy JS code this.clicked = false;
if (!(et2_compileLegacyJS(this.options.onclick, this, _node))()) return false;
{
this.clicked = false;
return false;
}
} }
// Submit the form // 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.getInstanceManager().submit(this); //TODO: this only needs to be passed if it's in a datagrid
} }
this.clicked = false; this.clicked = false;
return true;
}, },
set_label: function(_value) { set_label: function(_value) {

View File

@ -60,7 +60,7 @@ var et2_dropdown_button = et2_inputWidget.extend(
}, },
"onclick": { "onclick": {
"name": "onclick", "name": "onclick",
"type": "string", "type": "js",
"description": "JS code which gets executed when the button is clicked" "description": "JS code which gets executed when the button is clicked"
}, },
"select_options": { "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; this.clicked = true;
// Execute the JS code connected to the event handler if (!this._super.apply(this, arguments))
if (this.options.onclick)
{ {
// Exectute the legacy JS code this.clicked = false;
if (!(et2_compileLegacyJS(this.options.onclick, this, _node))()) 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; this.clicked = false;
return true;
}, },
onselect: function(event, selected_node) { onselect: function(event, selected_node) {

View File

@ -47,11 +47,6 @@ var et2_progress = et2_valueWidget.extend([et2_IDetachedDOM],
"type": "string", "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).", "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 "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"], legacyOptions: ["href", "extra_link_target", "imagemap", "extra_link_popup", "id"],

View File

@ -48,20 +48,17 @@ var et2_tree = et2_inputWidget.extend(
}, },
"onclick": { "onclick": {
"name": "onClick", "name": "onClick",
"type": "string", "type": "js",
"default": "",
"description": "JS code which gets executed when clicks on text of a node" "description": "JS code which gets executed when clicks on text of a node"
}, },
"onselect": { "onselect": {
"name": "onSelect", "name": "onSelect",
"type": "string", "type": "js",
"default": "",
"description": "Javascript executed when user selects a node" "description": "Javascript executed when user selects a node"
}, },
"oncheck": { "oncheck": {
"name": "onCheck", "name": "onCheck",
"type": "string", "type": "js",
"default": "",
"description": "Javascript executed when user checks a node" "description": "Javascript executed when user checks a node"
}, },
// onChange event is mapped depending on multiple to onCheck or onSelect // 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 // overwrite default onclick to do nothing, as we install onclick via dhtmlxtree
onclick: function(_node) {}, click: function(_node) {},
createTree: function(widget) { createTree: function(widget) {
widget.input = new dhtmlXTreeObject({ 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 // 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);}; 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) if (widget.options.autoloading)
{ {
var url = widget.options.autoloading; var url = widget.options.autoloading;
@ -217,6 +194,34 @@ var et2_tree = et2_inputWidget.extend(
widget.input.setDataMode('JSON'); 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) set_select_options: function(options)
{ {

View File

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

View File

@ -38,7 +38,7 @@
</grid> </grid>
</template> </template>
<template id="mail.index" template="" lang="" group="0" version="1.9.001"> <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"/> <html id="msg"/>
<buttononly id="button[mailcreate]" onclick="app.mail.mail_compose(false);" label="Compose" parent_node="mail-index_buttonmailcreate"/> <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"/> <buttononly id="button[testhtmlarea]" onclick="app.mail.mail_testhtmlarea(false);" label="Test HTML Area" parent_node="mail-index_buttontesthtmlarea"/>