diff --git a/Makefile b/Makefile
index 6a19d44c17d59ac5d70df52be423398a7df58c07..5b9f8cd41faabb8bcbfd50f3102a5a56aa4b9bd8 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@ BASE=ordernumber
 PLUGINTYPE=vmshopper
 VERSION=3.6.1
 
-PLUGINFILES=$(BASE).php $(BASE).script.php $(BASE).xml index.html $(BASE)/
+PLUGINFILES=$(BASE).php ordernumber_helper_joomla.php $(BASE).script.php $(BASE).xml index.html library/
 
 SYSTRANSLATIONS=$(call wildcard,language/*/*.plg_$(PLUGINTYPE)_$(BASE).*sys.ini)
 NONSYSTRANSLATIONS=${SYSTRANSLATIONS:%.sys.ini=%.ini}
diff --git a/fields/vmordernumbercounters.php b/fields/vmordernumbercounters.php
index 91da6154f9652d25557bc9d33061895fd116f994..f46a2be9c1e9de705c4323de1acdb13381710d41 100644
--- a/fields/vmordernumbercounters.php
+++ b/fields/vmordernumbercounters.php
@@ -78,11 +78,7 @@ class JFormFieldVmOrdernumberCounters extends JFormField {
 		$helper->loadjQuery();
 		$doc->addScript($helper->urlPath('js', 'ordernumber.js'));
         
-		// Look up the current counters
-		$db = JFactory::getDBO();
-		$db->setQuery('SELECT `number_format`, `count` FROM `#__virtuemart_shopper_plg_ordernumber` WHERE `number_type`='.$db->quote($this->countertype) . ' ORDER BY `number_format`;' );
-		$counters = $db->loadObjectList();
-       
+		$counters = $helper->getAllCounters($this->countertype);
 		return $helper->counter_modification_create_table ($this->countertype, $counters);
 	}
 }
diff --git a/ordernumber/assets/css/index.html b/library/css/index.html
similarity index 100%
rename from ordernumber/assets/css/index.html
rename to library/css/index.html
diff --git a/ordernumber/assets/css/ordernumber.css b/library/css/ordernumber.css
similarity index 99%
rename from ordernumber/assets/css/ordernumber.css
rename to library/css/ordernumber.css
index 953f955b119f348a03a3c4c2895dbbb239cf5cbe..f6e0862417aedcc44203d0ba0491be01031211de 100644
--- a/ordernumber/assets/css/ordernumber.css
+++ b/library/css/ordernumber.css
@@ -7,7 +7,7 @@ table.ordernumber-countertable {
 table.ordernumber-countertable.table-striped tbody > tr:nth-child(odd) > th {
     background: #E0E0E0;
 }
-.vmordernumber-btn {
+.ordernumber-btn {
     cursor: pointer;
 }
 
diff --git a/ordernumber/assets/images/icon-16-delete.png b/library/images/icon-16-delete.png
similarity index 100%
rename from ordernumber/assets/images/icon-16-delete.png
rename to library/images/icon-16-delete.png
diff --git a/ordernumber/assets/images/icon-16-edit.png b/library/images/icon-16-edit.png
similarity index 100%
rename from ordernumber/assets/images/icon-16-edit.png
rename to library/images/icon-16-edit.png
diff --git a/ordernumber/assets/images/icon-16-new.png b/library/images/icon-16-new.png
similarity index 100%
rename from ordernumber/assets/images/icon-16-new.png
rename to library/images/icon-16-new.png
diff --git a/ordernumber/assets/images/index.html b/library/images/index.html
similarity index 100%
rename from ordernumber/assets/images/index.html
rename to library/images/index.html
diff --git a/ordernumber/assets/images/loading.gif b/library/images/loading.gif
similarity index 100%
rename from ordernumber/assets/images/loading.gif
rename to library/images/loading.gif
diff --git a/ordernumber/assets/images/loading.png b/library/images/loading.png
similarity index 100%
rename from ordernumber/assets/images/loading.png
rename to library/images/loading.png
diff --git a/ordernumber/assets/index.html b/library/index.html
similarity index 100%
rename from ordernumber/assets/index.html
rename to library/index.html
diff --git a/ordernumber/assets/js/index.html b/library/js/index.html
similarity index 100%
rename from ordernumber/assets/js/index.html
rename to library/js/index.html
diff --git a/ordernumber/assets/js/ordernumber.js b/library/js/ordernumber.js
similarity index 76%
rename from ordernumber/assets/js/ordernumber.js
rename to library/js/ordernumber.js
index ea9e7fb5943ad63a21507fbf4b075da26fc5c74d..f05fc49715b8d69e69588b1d85b0dd6763c9b210 100644
--- a/ordernumber/assets/js/ordernumber.js
+++ b/library/js/ordernumber.js
@@ -1,3 +1,17 @@
+/**********************************************************************************
+ * The global ajax_ordernumber object should have the following entries:
+ *   - Translations: 
+ *     ORDERNUMBER_JS_NOT_AUTHORIZED, ORDERNUMBER_JS_INVALID_COUNTERVALUE, ORDERNUMBER_JS_JSONERROR
+ *     ORDERNUMBER_JS_NEWCOUNTER, ORDERNUMBER_JS_EDITCOUNTER, ORDERNUMBER_JS_DELETECOUNTER
+ *     ORDERNUMBER_JS_ADD_FAILED, ORDERNUMBER_JS_MODIFY_FAILED, ORDERNUMBER_JS_DELETE_FAILED
+ *   - ajax_url: The URL for all AJAX calls
+
+ * Optional entries (callback functions) are:
+ *  - updateMessages(messages, cssidentifier)
+ *  - parseAjaxResponse(response) => return json
+ *  - modifyAjaxArgs(ajaxargs)    => return ajaxargs with modified arguments for jquery.ajax calls
+ */
+ 
 /**********************************************************************************
  * 
  *  Javascript for the counter modification table
@@ -13,7 +27,8 @@ String.Format = function() {
 }
 
 var getCounterData = function (btn) {
-    return { row: jQuery(btn).closest("tr.counter_row") };
+    var row = jQuery(btn).closest("tr.counter_row");
+    return { row: row };
 }
 var handleJSONResponse = function (json, counter) {
 	if ('updateMessages' in ajax_ordernumber) { 
@@ -35,8 +50,7 @@ var ajaxEditCounter = function (btn, nrtype, ctr, value) {
     var value = NaN;
     var msgprefix = "";
     while (isNaN(value) && (value != null)) {
-        var editprompt = ajax_ordernumber.ORDERNUMBER_JS_EDITCOUNTER;
-        value = prompt (String.Format(editprompt, msgprefix, counter.counter, counter.value), counter.value);
+        value = prompt (String.Format(ajax_ordernumber.ORDERNUMBER_JS_EDITCOUNTER, msgprefix, counter.counter, counter.value), counter.value);
         if (value != null)
             value = parseInt(value);
         if (isNaN(value)) 
@@ -44,10 +58,8 @@ var ajaxEditCounter = function (btn, nrtype, ctr, value) {
     }
     if (value != null) {
         var loading = jQuery("img.ordernumber-loading").first().clone().insertAfter(btn).show();
-        jQuery.ajax({
+        var ajaxargs = {
             type: "POST",
-            cache: false,
-            dataType: "text", // Read text, but interpret as JSON later in the done method (prevents a warning!)
             url: ajax_ordernumber.ajax_url,
             data: { 
 				action: 'setCounter',
@@ -55,9 +67,11 @@ var ajaxEditCounter = function (btn, nrtype, ctr, value) {
 				counter: counter.counter, 
 				value: value 
 			},
-            success: function( data ) {
+			success: function ( json ) {
                 try {
-                    var json = jQuery.parseJSON(data);
+					if ('parseAjaxResponse' in ajax_ordernumber) { 
+						json = ajax_ordernumber.parseAjaxResponse(json);
+					}
                     handleJSONResponse(json, counter);
                 } catch (e) {
                     alert(ajax_ordernumber.ORDERNUMBER_JS_JSONERROR+"\n"+e);
@@ -73,7 +87,11 @@ var ajaxEditCounter = function (btn, nrtype, ctr, value) {
             },
             error: function() { alert (String.Format(ajax_ordernumber.ORDERNUMBER_JS_MODIFY_FAILED, counter.counter)); },
             complete: function() { jQuery(loading).remove(); },
-        });
+        };
+		if ('modifyAjaxArgs' in ajax_ordernumber) { 
+			ajaxargs = ajax_ordernumber.modifyAjaxArgs(ajaxargs);
+		}
+		jQuery.ajax(ajaxargs);
     }
 }
 var ajaxDeleteCounter = function (btn, nrtype, ctr, value) {
@@ -84,19 +102,20 @@ var ajaxDeleteCounter = function (btn, nrtype, ctr, value) {
     var proceed = confirm (String.Format(ajax_ordernumber.ORDERNUMBER_JS_DELETECOUNTER, counter.counter, counter.value));
     if (proceed == true) {
         var loading = jQuery("img.ordernumber-loading").first().clone().insertAfter(btn).show();
-        jQuery.ajax({
+        var ajaxargs = {
             type: "POST",
-            cache: false,
-            dataType: "text", // Read text, but interpret as JSON later in the done method (prevents a warning!)
+			dataType: "json",
             url: ajax_ordernumber.ajax_url,
             data: { 
 				action: 'deleteCounter',
 				nrtype: counter.type, 
 				counter: counter.counter 
 			},
-            success: function( data ) {
+			success: function ( json ) {
                 try {
-                    var json = jQuery.parseJSON(data);
+					if ('parseAjaxResponse' in ajax_ordernumber) { 
+						json = ajax_ordernumber.parseAjaxResponse(json);
+					}
                     handleJSONResponse(json, counter);
                 } catch (e) {
                     alert(ajax_ordernumber.ORDERNUMBER_JS_JSONERROR+"\n"+e);
@@ -110,7 +129,11 @@ var ajaxDeleteCounter = function (btn, nrtype, ctr, value) {
             },
             error: function() { alert (String.Format(ajax_ordernumber.ORDERNUMBER_JS_DELETE_FAILED, counter.counter)); },
             complete: function() { jQuery(loading).remove(); },
-        });
+        };
+		if ('modifyAjaxArgs' in ajax_ordernumber) { 
+			ajaxargs = ajax_ordernumber.modifyAjaxArgs(ajaxargs);
+		}
+		jQuery.ajax(ajaxargs);
     }
 }
 var ajaxAddCounter = function (btn, nrtype) {
@@ -118,20 +141,21 @@ var ajaxAddCounter = function (btn, nrtype) {
     var countername = prompt (ajax_ordernumber.ORDERNUMBER_JS_NEWCOUNTER);
     if (countername != null) {
         var loading = jQuery("img.ordernumber-loading").first().clone().insertAfter(jQuery(btn).find("img.ordernumber-counter-addbtn")).show();
-        jQuery.ajax({
+        var ajxargs = {
             type: "POST",
-            cache: false,
-            dataType: "text", // Read text, but interpret as JSON later in the done method (prevents a warning!)
+			dataType: "json",
             url: ajax_ordernumber.ajax_url,
             data: { 
 				action: "addCounter",
 				nrtype: nrtype, 
 				counter: countername 
 			},
-            success: function( data ) {
-                var json = data ? jQuery.parseJSON(data) : null;
+			
+			success: function ( json ) {
                 try {
-                    var json = jQuery.parseJSON(data);
+					if ('parseAjaxResponse' in ajax_ordernumber) { 
+						json = ajax_ordernumber.parseAjaxResponse(json);
+					}
                     handleJSONResponse(json, null);
                 } catch (e) {
                     alert(ajax_ordernumber.ORDERNUMBER_JS_JSONERROR+"\n"+e);
@@ -147,7 +171,11 @@ var ajaxAddCounter = function (btn, nrtype) {
             },
             error: function() { alert (String.Format(ajax_ordernumber.ORDERNUMBER_JS_ADD_FAILED, countername)); },
             complete: function() { jQuery(loading).remove(); },
-        });
+        };
+		if ('modifyAjaxArgs' in ajax_ordernumber) { 
+			ajaxargs = ajax_ordernumber.modifyAjaxArgs(ajaxargs);
+		}
+		jQuery.ajax(ajaxargs);
     }
 }
 
diff --git a/ordernumber_helper.php b/library/ordernumber_helper.php
similarity index 82%
rename from ordernumber_helper.php
rename to library/ordernumber_helper.php
index 49b407120d28e2c88d4d9b42b161b3a46da08daf..86c2bf50bb1798e335d3f129ec694198cf2e67da 100644
--- a/ordernumber_helper.php
+++ b/library/ordernumber_helper.php
@@ -6,9 +6,12 @@
  * @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 **/
 
-defined('_JEXEC') or	 die( 'Direct Access to ' . basename( __FILE__ ) . ' is not allowed.' ) ;
+if ( !defined( 'ABSPATH' ) && !defined('_JEXEC') ) { 
+	die( 'Direct Access to ' . basename( __FILE__ ) . ' is not allowed.' );
+}
 
 class OrdernumberHelper {
+	static $_version = "0.1";
 	protected $_callbacks = array();
 	public $_styles = array(
 		'counter-table-class' => "table-striped",
@@ -77,6 +80,37 @@ class OrdernumberHelper {
 		}
 	}
 
+	/**
+	 * Provide human-readable default values for the translatable strings.
+	 * Some systems use the translation key as fallback if no translation is found,
+	 * so we need to convert it to a human-readable value.
+	 */
+	public function readableString($string) {
+		static $readable_strings = array(
+			"PLG_ORDERNUMBER_COUNTERLIST_HEADER_VALUE"   => 'Counter value',
+			"PLG_ORDERNUMBER_COUNTERLIST_HEADER_COUNTER" => 'Counter name',
+			"PLG_ORDERNUMBER_COUNTERLIST_ADD"            => 'Add new counter',
+			"PLG_ORDERNUMBER_COUNTERLIST_GLOBAL"         => '[Global]',
+			"PLG_ORDERNUMBER_REPL_IFVAR"                 => 'If variable ...',
+			"PLG_ORDERNUMBER_REPL_IFVAL"                 => 'Value',
+			"PLG_ORDERNUMBER_REPL_SETVAR"                => 'Set variable ...',
+			"PLG_ORDERNUMBER_REPL_TOVAL"                 => 'to value ...',
+			"PLG_ORDERNUMBER_REPL_NOCUSTOMVARS"          => 'No custom variables have been defined.',
+			"PLG_ORDERNUMBER_REPL_ADDVAR"                => 'Add new custom variable',
+			"PLG_ORDERNUMBER_REPL_OP_NOCOND"             => 'No condition',
+			"PLG_ORDERNUMBER_REPL_OP_CONTAINS"           => 'contains',
+			"PLG_ORDERNUMBER_REPL_OP_STARTS"             => 'starts with',
+			"PLG_ORDERNUMBER_REPL_OP_ENDS"               => 'ends with'
+		);
+		// Use the human-readable text as default rather than the generic identifier.
+		// Otherwise, one always has to create a language file for every language, as 
+		// the fallback would be the identifier.
+		if (isset($readable_strings[$string]))
+			return $readable_strings[$string];
+		else
+			return $string;
+	}
+
 	public function urlPath($type, $file) {
 		if (isset($this->callbacks['urlPath'])) {
 			return $this->callbacks['urlPath']($type, $file);
@@ -107,6 +141,36 @@ class OrdernumberHelper {
 		}
 	}
 	
+	public function getAllCounters($type) {
+		if (isset($this->callbacks['getCounter'])) {
+			return $this->callbacks['getCounter']($type);
+		} else {
+			throw new Exception ('No callback defined for getAllCounters(type)!');
+		}
+	}
+	
+	public static function transposeCustomVariables($cvar) {
+		if (is_object($cvar)) 
+			$cvar = (array)$cvar;
+		if (!is_array($cvar))
+			$cvar = array();
+		// The customvars are stored in transposed form (for technical reasons, since there is no trigger 
+		// called when the corresponding form field from the plugin param is saved)
+		$customvars = array();
+        
+		if (!empty($cvar)) {
+			$keys = array_keys($cvar);
+			foreach (array_keys($cvar[$keys[0]]) as $i) {
+				$entry = array();
+				foreach ($keys as $k) {
+					$entry[$k] = $cvar[$k][$i];
+				}
+				$customvars[] = $entry;
+			}
+		}
+		return $customvars;
+	}
+	
 	/* Return a random "string" of the given length taken from the given alphabet */
 	protected static function randomString($alphabet, $len) {
 		$alen = strlen($alphabet);
@@ -242,6 +306,11 @@ class OrdernumberHelper {
 	}
 	
 	protected function extractCounterSettings ($fmt, $type, $ctrsettings) {
+		// Some e-Commerce systems use 'yes' for true, others use 1 => correct everything to 1
+		if ($ctrsettings["${type}_global"] == 'yes') {
+			$ctrsettings["${type}_global"] = 1;
+		}
+
 		// First, extract all counter settings, i.e. all strings of the form [#####:startval/increment] or [####/increment:startval]
 		$regexp = '%\[(#+)(/([0-9]+))?(:([0-9]+))?(/([0-9]+))?\]%';
 		
@@ -278,7 +347,7 @@ class OrdernumberHelper {
 		// If a separate counter format is given after the |, use it, otherwise reuse the number format itself as counter format
 		$parts = explode ("|", $fmt);
 		$ctrsettings["${type}_format"] = $parts[0];
-		$ctrsettings["${type}_counter"] = ($ctrsettings["${type}_global"]=='yes')?"":$parts[(count($parts)>1)?1:0];
+		$ctrsettings["${type}_counter"] = ($ctrsettings["${type}_global"]==1)?"":$parts[(count($parts)>1)?1:0];
 		
 		return $ctrsettings;
 	}
@@ -298,7 +367,7 @@ class OrdernumberHelper {
 		$format = $this->setupNumberFormatString($fmt, $type, $order, $reps);
 		$format = $this->doReplacements($format, $reps);
 		$ctrsettings = $this->extractCounterSettings ($format, $type, $ctrsettings);
-
+JFactory::getApplication()->enqueueMessage("<pre>Counter Settings: ".print_r($ctrsettings,1)."</pre>", 'error');
 // JFactory::getApplication()->enqueueMessage("<pre>Replacements for $type:".print_r($reps,1)."</pre>", 'error');
 		// Increment the counter only if the format contains a placeholder for it!
 		if (strpos($ctrsettings["${type}_format"], "#") !== false) {
@@ -322,34 +391,23 @@ class OrdernumberHelper {
 	 *     @param
 	 */
 	public function counter_modification_create_table($type, $counters) {
-/*		$pluginpath = '/plugins/shopper/ordernumber/ordernumber/';
-        $doc = JFactory::getDocument()->addStyleSheet(JURI::root(true) . $pluginpath . 'assets/css/ordernumber.css');
-        $this->makeJSTranslationsAvailable();
-        $this->loadjQuery();
-        $doc->addScript(JURI::root(true).$pluginpath . 'assets/js/ordernumber.js');
-        
-        // Look up the current counters
-        $db = JFactory::getDBO();
-        $db->setQuery('SELECT `number_format`, `count` FROM `#__virtuemart_shopper_plg_ordernumber` WHERE `number_type`='.$db->quote($type) . ' ORDER BY `number_format`;' );
-        $counters = $db->loadObjectList();
-        // Joomla 2.x uses <li> for the params and float:left on the controls, so we need to add that too
-        $float = "";
-        if (version_compare(JVERSION, '3.0', 'lt')) {
-            $float = "float: left; ";
-        }
-*/
         $html=array();
-        $html[] = "<img src='" . $this->urlPath ('images', 'loading.gif') . "' class='ordernumber-loading' style=\"display: none; position: absolute; top: 2px; left: 0px; z-index: 9999;\"/>";
+        $html[] = "<img src='" . $this->urlPath ('images', 'loading.gif') . "' class='ordernumber-loading' />";
         $html[] = "<table class=\"ordernumber-countertable " . $this->getStyle('counter-table-class') . "\" " . $this->getStyle('counter-table-style') . ">";
+        $html[] = "<thead>";
         $html[] = "	<tr>";
         $html[] = "		<th class='counter_format'>" . $this->__ ('PLG_ORDERNUMBER_COUNTERLIST_HEADER_COUNTER')."</th>";
         $html[] = "		<th class='counter_value'>" . $this->__ ('PLG_ORDERNUMBER_COUNTERLIST_HEADER_VALUE'). "</th>";
         $html[] = "		<th class='counter_buttons'></th>";
         $html[] = "	</tr>";
+        $html[] = "</thead>";
         $html[] = "	<colgroup><col class='counter_type'><col style=\"text-align: center\" ><col ></colgroup>";
+        $html[] = "<tbody>";
         foreach ($counters as $c) {
-            $html[] = $this->counter_modification_create_row ($type, $c->number_format, $c->count);
+            $html[] = $this->counter_modification_create_row ($type, $c->name, $c->value);
         }
+        $html[] = "</tbody>";
+        $html[] = "<tfoot>";
         $html[] = "	<tr class='addcounter_row'>";
         $html[] = "		<td colspan=3 class='counter_add'>";
         $html[] = "			<div class='ordernumber-counter-addbtn ordernumber-btn' onClick='ajaxAddCounter(this, " . json_encode($type).")'>";
@@ -359,6 +417,7 @@ class OrdernumberHelper {
         $html[] = "			</div>";
         $html[] = "		</td>";
         $html[] = "  </tr>";
+        $html[] = "</tfoot>";
         $html[] = "</table>";
         return implode("\n", $html);
     }
@@ -366,16 +425,16 @@ class OrdernumberHelper {
     public function counter_modification_create_row ($type, $counter, $value) {
 		$html=array();
 		$html[] = "	<tr class='counter_row counter_row_$type'>";
-		$html[] = "		<td class='counter_format'>" . (string)(($counter=="")?($this->__ ('PLG_ORDERNUMBER_COUNTERLIST_GLOBAL')):$counter) . "</td>";
-		$html[] = "		<td class='counter_value'>" . (string)$value . "</td>";
+		$html[] = "		<td class='counter_format'>" . htmlentities((string)(($counter=="")?($this->__ ('PLG_ORDERNUMBER_COUNTERLIST_GLOBAL')):$counter)) . "</td>";
+		$html[] = "		<td class='counter_value'>" . htmlentities((string)$value) . "</td>";
 		$html[] = "		<td class='counter_buttons'>";
 		$html[] = "			<div class='ordernumber-ajax-loading'>";
 		$html[] = "				<img src='" . $this->urlPath('images', 'icon-16-edit.png') . "' class='ordernumber-counter-editbtn ordernumber-btn' ";
-		$html[] = "					onClick='ajaxEditCounter(this, " . json_encode($type) . ", ".json_encode($counter).", $value)' />";
+		$html[] = "					onClick='ajaxEditCounter(this, " . json_encode($type) . ", ".json_encode($counter).", " . json_encode($value). ")' />";
 		$html[] = "			</div>";
 		$html[] = "			<div class='ordernumber-ajax-loading'>";
 		$html[] = "				<img src='" . $this->urlPath ('images', 'icon-16-delete.png') . "' class='ordernumber-counter-deletebtn ordernumber-btn' ";
-		$html[] = "					onClick='ajaxDeleteCounter(this, ".json_encode($type).", ".json_encode($counter).", $value)' />";
+		$html[] = "					onClick='ajaxDeleteCounter(this, ".json_encode($type).", ".json_encode($counter).", " . json_encode($value) . ")' />";
 		$html[] = "			</div>";
 		$html[] = "		</td>";
 		$html[] = "	</tr>";
@@ -465,16 +524,16 @@ class OrdernumberHelper {
         );
         $html  = '
         <tr>
-        	<td class="variables_ifvar"><input name="' . $name . '[conditionvar][]" value="' . (isset($values['conditionvar'])?$values['conditionvar']:'') . '" ' . $disabled . '/></td>
-        	<td class="variables_ifop"      ><select name="' . $name . '[conditionop][]" ' . $disabled . ' style="width: 100px">';
+        	<td class="variables_ifvar"><input name="' . $name . '[conditionvar][]" value="' . (isset($values['conditionvar'])?htmlentities($values['conditionvar']):'') . '" ' . htmlentities($disabled) . '/></td>
+        	<td class="variables_ifop"      ><select name="' . $name . '[conditionop][]" ' . htmlentities($disabled) . ' style="width: 100px">';
         foreach ($operators as $op => $opname) {
         	$html .= '		<option value="' . $op . '" ' . (($op === $operator)?'selected':'') . '>' . htmlspecialchars($opname) . '</option>';
         }
         $html .= '</select></td>
-        	<td class="variables_ifval"   ><input name="' . $name . '[conditionval][]" value="' . (isset($values['conditionval'])?$values['conditionval']:'') . '" ' . $disabled . '/></td>
+        	<td class="variables_ifval"   ><input name="' . $name . '[conditionval][]" value="' . (isset($values['conditionval'])?$values['conditionval']:'') . '" ' . htmlentities($disabled) . '/></td>
         	<td class="variables_then">=></td>
-        	<td class="variables_thenvar"><input name="' . $name . '[newvar][]"       value="' . (isset($values['newvar'])?$values['newvar']:'') .       '" ' . $disabled . '/></td>
-        	<td class="variables_thenval"><input name="' . $name . '[newval][]"       value="' . (isset($values['newval'])?$values['newval']:'') .       '" ' . $disabled . '/></td>
+        	<td class="variables_thenvar"><input name="' . $name . '[newvar][]"       value="' . (isset($values['newvar'])?$values['newvar']:'') .       '" ' . htmlentities($disabled) . '/></td>
+        	<td class="variables_thenval"><input name="' . $name . '[newval][]"       value="' . (isset($values['newval'])?$values['newval']:'') .       '" ' . htmlentities($disabled) . '/></td>
         	<td class="sort"></td>
         	<td class="variables_settings"><img src="' . $this->urlPath('images', 'icon-16-delete.png' ) . '" class="ordernumber-replacement-deletebtn ordernumber-btn"></td>
         </tr>';
diff --git a/ordernumber.php b/ordernumber.php
index 40abd09ee24c9997144bd130c92ed27248976753..5d66981afc18b92e38b4381175788ba4717b84fa 100644
--- a/ordernumber.php
+++ b/ordernumber.php
@@ -66,12 +66,12 @@ class plgVmShopperOrdernumber extends vmShopperPlugin {
 
 
 
-    protected function setupStoreReplacements (&$reps, $details, $nrtype) {
+    public function setupStoreReplacements (&$reps, $details, $nrtype) {
         if (isset($details->virtuemart_vendor_id)) 
             $reps["[vendorid]"] = $details->virtuemart_vendor_id;
     }
     
-    protected function setupAddressReplacements(&$reps, $prefix, $details, $nrtype) {
+    public function setupAddressReplacements(&$reps, $prefix, $details, $nrtype) {
         if (isset($details->email))       $reps["[email]"] = $details->email;
         if (isset($details->title))       $reps["[title]"] = $details->title;
         if (isset($details->first_name))  $reps["[firstname]"] = $details->first_name;
@@ -101,7 +101,7 @@ class plgVmShopperOrdernumber extends vmShopperPlugin {
         }
     }
     
-    protected function setupOrderReplacements (&$reps, $details, $nrtype) {
+    public function setupOrderReplacements (&$reps, $details, $nrtype) {
         // Customer numbers are created before any order is submitted, so we don't have any information available.
         if ($nrtype=='customer_number') 
 			return;
@@ -177,7 +177,7 @@ class plgVmShopperOrdernumber extends vmShopperPlugin {
 		
     }
    
-    protected function setupUserReplacements (&$reps, $details, $nrtype) {
+    public function setupUserReplacements (&$reps, $details, $nrtype) {
         // TODO: Implement shopper group!
         $reps["[userid]"]      = $details->virtuemart_user_id;
         if (isset($details->ip_address))     $reps["[ipaddress]"] = $details->ip_address;
@@ -187,12 +187,12 @@ class plgVmShopperOrdernumber extends vmShopperPlugin {
         if (isset($details->user_is_vendor)) $reps["[user_is_vendor]"] = $details->user_is_vendor;
     }
     
-    protected function setupShippingReplacements(&$reps, $order, $nrtype) {
+    public function setupShippingReplacements(&$reps, $order, $nrtype) {
 		if (isset($details->virtuemart_paymentmethod_id))		$reps['[paymentmethod]'] = $details->virtuemart_paymentmethod_id;
 		if (isset($details->virtuemart_shipmentmethod_id))		$reps['[shipmentmethod]'] = $details->virtuemart_shipmentmethod_id;
     }
     
-    protected function setupThirdPartyReplacements (&$reps, $details, $nrtype) {
+    public function setupThirdPartyReplacements (&$reps, $details, $nrtype) {
         JPluginHelper::importPlugin('vmshopper');
         JDispatcher::getInstance()->trigger('onVmOrdernumberGetVariables',array(&$reps, $nrtype, $details));
     }
diff --git a/ordernumber.xml b/ordernumber.xml
index c6cd63418adbad6fe2c97969ae6652e028ea555d..d15b619d866181cc1021e628b498a4073263d207 100644
--- a/ordernumber.xml
+++ b/ordernumber.xml
@@ -16,11 +16,13 @@
 
     <files>
         <filename plugin="ordernumber">ordernumber.php</filename>
+        <filename>ordernumber_helper.php</filename>
+        <filename>ordernumber_helper_joomla.php</filename>
         <filename>ordernumber.script.php</filename>
         <filename>index.html</filename>
         <folder>language</folder>
         <folder>fields</folder>
-        <folder>ordernumber</folder>
+        <folder>library</folder>
     </files>
     <scriptfile>ordernumber.script.php</scriptfile>
     <languages folder="language">
diff --git a/ordernumber/index.html b/ordernumber/index.html
deleted file mode 100644
index 2efb97f319a35f6bd80f1751134ed71ec11888eb..0000000000000000000000000000000000000000
--- a/ordernumber/index.html
+++ /dev/null
@@ -1 +0,0 @@
-<!DOCTYPE html><title></title>
diff --git a/ordernumber_helper_joomla.php b/ordernumber_helper_joomla.php
index c0152977edba724146434eb08fa83c8f6283b0c3..88d940f548fd133d2465f919bd078933f73f3f9f 100644
--- a/ordernumber_helper_joomla.php
+++ b/ordernumber_helper_joomla.php
@@ -13,7 +13,7 @@ if (!class_exists( 'VmConfig' ))
 VmConfig::loadConfig();
 
 if (!class_exists( 'OrdernumberHelper' )) 
-	require_once (dirname(__FILE__) . DS . 'ordernumber_helper.php');
+	require_once (dirname(__FILE__) . DS . 'library' . DS . 'ordernumber_helper.php');
 
 class OrdernumberHelperJoomla extends OrdernumberHelper {
 	public $tableName = '';
@@ -74,37 +74,23 @@ class OrdernumberHelperJoomla extends OrdernumberHelper {
 	}
     
 
-	public static function transposeCustomVariables($cvar) {
-		if (is_object($cvar)) 
-			$cvar = (array)$cvar;
-		if (!is_array($cvar))
-			$cvar = array();
-		// The customvars are stored in transposed form (for technical reasons, since there is no trigger 
-		// called when the corresponding form field from the plugin param is saved)
-		$customvars = array();
-        
-		if (!empty($cvar)) {
-			$keys = array_keys($cvar);
-			foreach (array_keys($cvar[$keys[0]]) as $i) {
-				$entry = array();
-				foreach ($keys as $k) {
-					$entry[$k] = $cvar[$k][$i];
-				}
-				$customvars[] = $entry;
-			}
-		}
-		return $customvars;
-	}
 	
 	public function __($string) {
 // print("<pre>translating: $string</pre>");
 		return JText::_($string);
 	}
     function urlPath($type, $file) {
-		static $pluginpath = '/plugins/vmshopper/ordernumber/ordernumber/';
-		return JURI::root(true) . $pluginpath . 'assets/' . $type . '/' . $file;
+		static $pluginpath = '/plugins/vmshopper/ordernumber/library/';
+		return JURI::root(true) . $pluginpath . $type . '/' . $file;
     }
  	
+ 	function getAllCounters($type) {
+		// Look up the current counters
+		$db = JFactory::getDBO();
+		$db->setQuery('SELECT `number_type` AS `type`, `number_format` AS `name`, `count` AS `value` FROM `#__virtuemart_shopper_plg_ordernumber` WHERE `number_type`='.$db->quote($type) . ' ORDER BY `number_format`;' );
+		return $db->loadObjectList();
+ 	}
+ 	
 	function getCounter($nrtype, $format, $default=0) {
 		$db = JFactory::getDBO();
         
@@ -160,7 +146,10 @@ class OrdernumberHelperJoomla extends OrdernumberHelper {
 		// Extract the messages from the returned string, add the ordernumber-message class (so the next ajax call
 		// can remove them again) and then move the messages to the original message container.
 		// Things are complicated by the fact that no #system-message element exists if no messages were printed so far
-		return 'ajax_ordernumber.updateMessages = function(messages, area) {
+		// Also:
+		// Request response as text, but interpret as JSON later in the done method (prevents a warning due to Joomla/VM's botched json implementation!)
+		return '
+ajax_ordernumber.updateMessages = function(messages, area) {
     jQuery( "#system-message-container #system-message ."+area+"-message").remove();
     var newmessages = jQuery( messages ).find("div.alert, .message").addClass(area+"-message");
     if (!jQuery( "#system-message-container #system-message").length && newmessages.length) {
@@ -171,6 +160,14 @@ class OrdernumberHelperJoomla extends OrdernumberHelper {
         }
     }
     newmessages.appendTo( "#system-message-container #system-message");
+}
+ajax_ordernumber.parseAjaxResponse = function(json) {
+	return jQuery.parseJSON(json);
+}
+ajax_ordernumber.modifyAjaxArgs = function (ajaxargs) {
+	ajaxargs.dataType="text";
+	ajaxargs.cache=false;
+	return ajaxargs;
 }';
 	}