From e4ed4399cf201b7c5b84c374080322bd4645dd05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20St=C3=B6ckel?= Date: Tue, 6 Mar 2012 15:26:11 +0000 Subject: [PATCH] Attempt of fixing legacy JavaScript functions, only tested for simple cases --- etemplate/js/et2_core_baseWidget.js | 8 +- etemplate/js/et2_core_common.js | 108 ----------------- etemplate/js/et2_core_inputWidget.js | 4 +- etemplate/js/et2_core_legacyJSFunctions.js | 135 +++++++++++++++++++++ etemplate/js/et2_extension_nextmatch.js | 17 +-- etemplate/js/et2_widget_button.js | 19 +-- etemplate/js/et2_widget_progress.js | 2 +- etemplate/js/etemplate2.js | 1 + 8 files changed, 151 insertions(+), 143 deletions(-) create mode 100644 etemplate/js/et2_core_legacyJSFunctions.js diff --git a/etemplate/js/et2_core_baseWidget.js b/etemplate/js/et2_core_baseWidget.js index 417eb5f857..ce057b8883 100644 --- a/etemplate/js/et2_core_baseWidget.js +++ b/etemplate/js/et2_core_baseWidget.js @@ -47,7 +47,7 @@ var et2_baseWidget = et2_DOMWidget.extend(et2_IAligned, { }, "onclick": { "name": "onclick", - "type": "js", + "type": "string", "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) { $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); }, - click: function(event) { + click: function(_node) { if (this.onclick) { - return this.onclick.call(this, event); + return this.onclick(_node); } return true; diff --git a/etemplate/js/et2_core_common.js b/etemplate/js/et2_core_common.js index 9de0fa5554..a3e3f7ca9e 100644 --- a/etemplate/js/et2_core_common.js +++ b/etemplate/js/et2_core_common.js @@ -80,70 +80,6 @@ function et2_form_name(_cname,_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],"''",$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 * into the corresponding type. The (converted) value is returned. All supported @@ -193,50 +129,6 @@ function et2_checkType(_val, _type, _attr, _cname) 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" if (_type == "string") { diff --git a/etemplate/js/et2_core_inputWidget.js b/etemplate/js/et2_core_inputWidget.js index 14422cb8c3..4de0f0f92f 100644 --- a/etemplate/js/et2_core_inputWidget.js +++ b/etemplate/js/et2_core_inputWidget.js @@ -41,7 +41,7 @@ var et2_inputWidget = et2_valueWidget.extend(et2_IInput, { }, "onchange": { "name": "onchange", - "type": "js", + "type": "string", "description": "JS code which is executed when the value changes." }, "validation_error": { @@ -113,7 +113,7 @@ var et2_inputWidget = et2_valueWidget.extend(et2_IInput, { change: function(_node) { if (this.onchange) { - return this.onchange.apply(_node); + return this.onchange(_node); } }, diff --git a/etemplate/js/et2_core_legacyJSFunctions.js b/etemplate/js/et2_core_legacyJSFunctions.js new file mode 100644 index 0000000000..ce06e0ba9c --- /dev/null +++ b/etemplate/js/et2_core_legacyJSFunctions.js @@ -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],"''",$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); + diff --git a/etemplate/js/et2_extension_nextmatch.js b/etemplate/js/et2_extension_nextmatch.js index 53e84d3b96..aeb32298ba 100644 --- a/etemplate/js/et2_extension_nextmatch.js +++ b/etemplate/js/et2_extension_nextmatch.js @@ -953,20 +953,11 @@ var et2_nextmatch_header_bar = Class.extend(et2_INextmatchHeader, { var input = select.input; if(this.nextmatch.options.settings[name+"_onchange"]) { + // Get the onchange function string var onchange = this.nextmatch.options.settings[name+"_onchange"]; - // onchange needs to get current values - if(typeof onchange == "string") { - // Don't change original so we can do this again - 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); - }); + + // Connect it to the onchange event of the input element + input.change(this.nextmatch, et2_compileLegacyJS(onchange, this.nextmatch, input)); } else { diff --git a/etemplate/js/et2_widget_button.js b/etemplate/js/et2_widget_button.js index 7f21a984bd..338c5a2639 100644 --- a/etemplate/js/et2_widget_button.js +++ b/etemplate/js/et2_widget_button.js @@ -37,7 +37,7 @@ var et2_button = et2_baseWidget.extend([et2_IInput, et2_IDetachedDOM], { }, "onclick": { "name": "onclick", - "type": "js", + "type": "string", "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; }, - onclick: function(e) { + onclick: function(_node) { // Execute the JS code connected to the event handler if (this.options.onclick) { - // onclick needs to get current values - if(typeof this.options.onclick == "string") { - // 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()) + // Exectute the legacy JS code + if (!(et2_compileLegacyJS(this.options.onclick, this, _node))()) { return false; } diff --git a/etemplate/js/et2_widget_progress.js b/etemplate/js/et2_widget_progress.js index 9480257ea0..e3780f1e54 100644 --- a/etemplate/js/et2_widget_progress.js +++ b/etemplate/js/et2_widget_progress.js @@ -48,7 +48,7 @@ var et2_progress = et2_valueWidget.extend([et2_IDetachedDOM], }, "onchange": { "name": "onchange", - "type": "js", + "type": "string", "description": "JS code which is executed when the value changes." } }, diff --git a/etemplate/js/etemplate2.js b/etemplate/js/etemplate2.js index d95f3a4885..709c78b052 100644 --- a/etemplate/js/etemplate2.js +++ b/etemplate/js/etemplate2.js @@ -42,6 +42,7 @@ et2_core_xml; et2_core_arrayMgr; et2_core_interfaces; + et2_core_legacyJSFunctions; // Include the client side api core jsapi.egw_core;