Attempt of fixing legacy JavaScript functions, only tested for simple cases

This commit is contained in:
Andreas Stöckel 2012-03-06 15:26:11 +00:00
parent d996537c35
commit e4ed4399cf
8 changed files with 151 additions and 143 deletions

View File

@ -47,7 +47,7 @@ var et2_baseWidget = et2_DOMWidget.extend(et2_IAligned, {
}, },
"onclick": { "onclick": {
"name": "onclick", "name": "onclick",
"type": "js", "type": "string",
"description": "JS code which is executed when the element is clicked." "description": "JS code which is executed when the element is clicked."
} }
}, },
@ -200,7 +200,7 @@ var et2_baseWidget = et2_DOMWidget.extend(et2_IAligned, {
if (this.node) if (this.node)
{ {
$j(this.node).bind("click.et2_baseWidget", this, function(e) { $j(this.node).bind("click.et2_baseWidget", this, function(e) {
return e.data.click.call(e.data,e); return e.data.click.call(e.data, this);
}); });
} }
@ -232,10 +232,10 @@ var et2_baseWidget = et2_DOMWidget.extend(et2_IAligned, {
return this.getDOMNode(this); return this.getDOMNode(this);
}, },
click: function(event) { click: function(_node) {
if (this.onclick) if (this.onclick)
{ {
return this.onclick.call(this, event); return this.onclick(_node);
} }
return true; return true;

View File

@ -80,70 +80,6 @@ function et2_form_name(_cname,_name)
return parts.length ? name + '['+parts.join('][')+']' : name; return parts.length ? name + '['+parts.join('][')+']' : name;
} }
/**
* Resolve javascript pseudo functions in onclick or onchange:
* - egw::link('$l','$p') calls egw.link($l,$p)
* - form::name('name') returns expanded name/id taking into account the name at that point of the template hierarchy
* - egw::lang('Message ...') translate the message, calls egw.lang()
* - confirm('message') translates 'message' and adds a '?' if not present
* - window.open() replaces it with egw_openWindowCentered2()
* - xajax_doXMLHTTP('etemplate. replace ajax calls in widgets with special handler not requiring etemplate run rights
*
* @param string _val onclick, onchange, ... action
* @param string _cname name-prefix / name-space
* @return string
*/
function et2_js_pseudo_funcs(_val, _cname)
{
// TODO: Call et2 specific egw instance
// Move this function to the API!
if (_val.indexOf('egw::link(') != -1)
{
_val = _val.replace(/egw::link\(/g,'egw.link(');
}
if (_val.indexOf('form::name(') != -1)
{
_val = _val.replace(/form::name\(/g,_cname ? "et2_form_name('"+_cname+"'," : '(');
}
if (_val.indexOf('egw::lang(') != -1)
{
_val = _val.replace(/egw::lang\(/g,'egw.lang(');
}
// ToDo: inserts the styles of a named template
/*if (preg_match('/template::styles\(["\']{1}(.*)["\']{1}\)/U',$on,$matches))
{
$tpl = $matches[1] == $this->name ? $this : new etemplate($matches[1]);
$on = str_replace($matches[0],"'<style>".str_replace(array("\n","\r"),'',$tpl->style)."</style>'",$on);
}*/
// translate messages in confirm()
if (_val.indexOf('confirm(') != -1)
{
_val = _val.replace(/confirm\((['"])(.*?)(\?)?['"]\)/,"confirm(egw.lang($1$2$1)+'$3')"); // add ? if not there, saves extra phrase
}
// replace window.open() with EGw's egw_openWindowCentered2()
if (_val.indexOf('window.open(') != -1)
{
_val = _val.replace(/window.open\('(.*)','(.*)','dependent=yes,width=([^,]*),height=([^,]*),scrollbars=yes,status=(.*)'\)/,
"egw_openWindowCentered2('$1', '$2', $3, $4, '$5')");
}
// replace xajax calls to code in widgets, with the "etemplate" handler,
// this allows to call widgets with the current app, otherwise everyone would need etemplate run rights
if (_val.indexOf("xajax_doXMLHTTP('etemplate.") != -1)
{
_val = _val.replace(/^xajax_doXMLHTTP\('etemplate\.([a-z]+_widget\.[a-zA-Z0-9_]+)\'/,
"xajax_doXMLHTTP('"+egw.getAppName()+".$1.etemplate'");
}
return _val;
}
/** /**
* 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
@ -193,50 +129,6 @@ function et2_checkType(_val, _type, _attr, _cname)
return _err(); return _err();
} }
if (_type == "js")
{
// Handle the default case
if (_val === null)
{
return null;
}
if (_val instanceof Function)
{
return _val;
}
// Create a new function containing the code
if (typeof _val == "string")
{
if (_val !== "")
{
try
{
// Parse JS code properly
_val = et2_js_pseudo_funcs(_val, _cname);
if(_val == "1") return function() {return true;};
// Check for remaining row data
if(_val.indexOf("$") >= 0 || _val.indexOf("@") >= 0)
{
// Still needs parsing
return _val;
}
return new Function(_val);
}
catch(e)
{
egw.debug("error", "Error while parsing JS event handler code", e,_val);
}
}
return null;
}
return _err();
}
// Check whether the given value is of the type "string" // Check whether the given value is of the type "string"
if (_type == "string") if (_type == "string")
{ {

View File

@ -41,7 +41,7 @@ var et2_inputWidget = et2_valueWidget.extend(et2_IInput, {
}, },
"onchange": { "onchange": {
"name": "onchange", "name": "onchange",
"type": "js", "type": "string",
"description": "JS code which is executed when the value changes." "description": "JS code which is executed when the value changes."
}, },
"validation_error": { "validation_error": {
@ -113,7 +113,7 @@ var et2_inputWidget = et2_valueWidget.extend(et2_IInput, {
change: function(_node) { change: function(_node) {
if (this.onchange) if (this.onchange)
{ {
return this.onchange.apply(_node); return this.onchange(_node);
} }
}, },

View File

@ -0,0 +1,135 @@
/**
* eGroupWare eTemplate2 - A simple PHP expression parser written in JS
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2011
* @version $Id: et2_core_phpExpressionCompiler.js 38256 2012-03-05 13:07:38Z igel457 $
*/
"use strict";
/*egw:uses
et2_interfaces;
et2_core_common;
*/
(function() {
/**
* Resolve javascript pseudo functions in onclick or onchange:
* - egw::link('$l','$p') calls egw.link($l,$p)
* - form::name('name') returns expanded name/id taking into account the name at that point of the template hierarchy
* - egw::lang('Message ...') translate the message, calls egw.lang()
* - confirm('message') translates 'message' and adds a '?' if not present
* - window.open() replaces it with egw_openWindowCentered2()
* - xajax_doXMLHTTP('etemplate. replace ajax calls in widgets with special handler not requiring etemplate run rights
*
* @param string _val onclick, onchange, ... action
* @param string _cname name-prefix / name-space
* @return string
*/
function js_pseudo_funcs(_val)
{
if (_val.indexOf('egw::link(') != -1)
{
_val = _val.replace(/egw::link\(/g,'egw.link(');
}
if (_val.indexOf('form::name(') != -1)
{
// XXX Use the widget reference XXX
//_val = _val.replace(/form::name\(/g,_cname ? "et2_form_name('"+_cname+"'," : '(');
}
if (_val.indexOf('egw::lang(') != -1)
{
_val = _val.replace(/egw::lang\(/g,'egw.lang(');
}
// ToDo: inserts the styles of a named template
/*if (preg_match('/template::styles\(["\']{1}(.*)["\']{1}\)/U',$on,$matches))
{
$tpl = $matches[1] == $this->name ? $this : new etemplate($matches[1]);
$on = str_replace($matches[0],"'<style>".str_replace(array("\n","\r"),'',$tpl->style)."</style>'",$on);
}*/
// translate messages in confirm()
if (_val.indexOf('confirm(') != -1)
{
_val = _val.replace(/confirm\((['"])(.*?)(\?)?['"]\)/,"confirm(egw.lang($1$2$1)+'$3')"); // add ? if not there, saves extra phrase
}
// replace window.open() with EGw's egw_openWindowCentered2()
if (_val.indexOf('window.open(') != -1)
{
_val = _val.replace(/window.open\('(.*)','(.*)','dependent=yes,width=([^,]*),height=([^,]*),scrollbars=yes,status=(.*)'\)/,
"egw_openWindowCentered2('$1', '$2', $3, $4, '$5')");
}
// replace xajax calls to code in widgets, with the "etemplate" handler,
// this allows to call widgets with the current app, otherwise everyone would need etemplate run rights
if (_val.indexOf("xajax_doXMLHTTP('etemplate.") != -1)
{
_val = _val.replace(/^xajax_doXMLHTTP\('etemplate\.([a-z]+_widget\.[a-zA-Z0-9_]+)\'/,
"xajax_doXMLHTTP('"+egw.getAppName()+".$1.etemplate'");
}
return _val;
}
this.et2_compileLegacyJS = function(_code, _widget, _context) {
// Replace the javascript pseudo-functions
_code = js_pseudo_funcs(_code);
// Check whether _code is simply "1" -- if yes replace it accordingly
if (_code === '1')
{
_code = 'return true;';
}
// Check whether some pseudo-variables still reside inside of the code,
// if yes, replace them.
if (_code.indexOf("$") >= 0 || _code.indexOf("@") >= 0)
{
// Get the content array manager for the widget
var mgr = _widget.getArrayMgr("content");
if (mgr)
{
_code = mgr.expandName(_code);
}
}
// Context is the context in which the function will run. Set context to
// null as a default, so that it's possible to find bugs where "this" is
// accessed in the code, but not properly set.
var context = _context ? _context : null;
// Check whether the given widget implements the "et2_IDOMNode"
// interface
if (!context && _widget.implements(et2_IDOMNode))
{
context = _widget.getDOMNode();
}
// Generate the function itself
var func = new Function('egw', 'widget', 'window', _code);
// Execute the code and return its results, pass the egw instance and
// the widget
return function() {
// Get the egw reference
var egw = _widget.egw();
// 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);
}
}
}).call(window);

View File

@ -953,20 +953,11 @@ var et2_nextmatch_header_bar = Class.extend(et2_INextmatchHeader, {
var input = select.input; var input = select.input;
if(this.nextmatch.options.settings[name+"_onchange"]) if(this.nextmatch.options.settings[name+"_onchange"])
{ {
// Get the onchange function string
var onchange = this.nextmatch.options.settings[name+"_onchange"]; var onchange = this.nextmatch.options.settings[name+"_onchange"];
// onchange needs to get current values
if(typeof onchange == "string") { // Connect it to the onchange event of the input element
// Don't change original so we can do this again input.change(this.nextmatch, et2_compileLegacyJS(onchange, this.nextmatch, input));
onchange = et2_js_pseudo_funcs(onchange, this.nextmatch.id);
if(onchange.indexOf("$") >= 0 || onchange.indexOf("@") >= 0) {
var mgr = this.nextmatch.getArrayMgr("content");
if(mgr) onchange = mgr.expandName(onchange);
}
onchange = new Function(onchange);
}
input.change(this.nextmatch, function(event) {
onchange(event);
});
} }
else else
{ {

View File

@ -37,7 +37,7 @@ var et2_button = et2_baseWidget.extend([et2_IInput, et2_IDetachedDOM], {
}, },
"onclick": { "onclick": {
"name": "onclick", "name": "onclick",
"type": "js", "type": "string",
"description": "JS code which gets executed when the button is clicked" "description": "JS code which gets executed when the button is clicked"
} }
}, },
@ -89,23 +89,12 @@ var et2_button = et2_baseWidget.extend([et2_IInput, et2_IDetachedDOM], {
return this.btn ? this.btn[0] : null; return this.btn ? this.btn[0] : null;
}, },
onclick: function(e) { onclick: function(_node) {
// Execute the JS code connected to the event handler // Execute the JS code connected to the event handler
if (this.options.onclick) if (this.options.onclick)
{ {
// onclick needs to get current values // Exectute the legacy JS code
if(typeof this.options.onclick == "string") { if (!(et2_compileLegacyJS(this.options.onclick, this, _node))())
// Don't change this.options.onclick so we can do this again
var onclick = et2_js_pseudo_funcs(this.options.onclick, this.id);
if(onclick.indexOf("$") >= 0 || onclick.indexOf("@") >= 0) {
var mgr = this.getArrayMgr("content");
if(mgr) onclick = mgr.expandName(onclick);
}
onclick = new Function(onclick);
if(!onclick())
return false;
}
else if (!this.options.onclick())
{ {
return false; return false;
} }

View File

@ -48,7 +48,7 @@ var et2_progress = et2_valueWidget.extend([et2_IDetachedDOM],
}, },
"onchange": { "onchange": {
"name": "onchange", "name": "onchange",
"type": "js", "type": "string",
"description": "JS code which is executed when the value changes." "description": "JS code which is executed when the value changes."
} }
}, },

View File

@ -42,6 +42,7 @@
et2_core_xml; et2_core_xml;
et2_core_arrayMgr; et2_core_arrayMgr;
et2_core_interfaces; et2_core_interfaces;
et2_core_legacyJSFunctions;
// Include the client side api core // Include the client side api core
jsapi.egw_core; jsapi.egw_core;