Jump to content
  • Checkout
  • Login
  • Get in touch

osCommerce

The e-commerce.

jQuery/Ajax Advanced Order Handler for osCommerce 2.3.3


Recommended Posts

Hi Jonas

 

Thanks for your reply!

Me too, i don't use all functions since i don't need all of them. And if there are mini bugs i just go around them.  ;)

The basic functions work and that's what counts. I just meant it more to help you hunt down remaining bugs. Which most of them are browser related (stupid FF! <_< )

I don't think that there ever will be a 100% clean, bug free order editor unless it is a commercial add-on.

Like i mentioned the effort and support you put in your add-ons is exceptional and deserves respect.

 

Regarding Bug 4, very clever! Didn't think about it that way.

Bug 5, i'll check it out. It is not a super vital function though.

 

Thanks again and kind regards

Lambros

Edited by Tsimi
Link to comment
Share on other sites

Hi Jonas

 

Thanks for your reply!

Me too, i don't use all functions since i don't need all of them. And if there are mini bugs i just go around them.  ;)

The basic functions work and that's what counts. I just meant it more to help you hunt down remaining bugs. Which most of them are browser related (stupid FF! <_< )

 

 

I really appreciate that you and others spend time to report bugs and give feedback. Without any response here on the forum I would quickly grow tired of making Add-Ons. So please do continue to report the mini bugs that you find. ha ha :thumbsup:

 

 

 

I don't think that there ever will be a 100% clean, bug free order editor unless it is a commercial add-on.

 

I have to say that I disagree, since when did commercial products outperform Open Source projects?  :P

I can't say that I'm very impressed with most of the commercial tools I use in my business, even though there are exceptions of course. They simply lack creativity and only have the absolutely base functionality which often leaves a lot to be desired ..

 

I'm certain that there can be a 100% working order editor which fits all needs. This, of course, requires that there aren't any prior errors on the osC installation when the order editor is installed. It looks like a lot of people have broken/deprecated Javascript code from other Add-Ons which can and probably will break the order handler.

 

I'll guess there's always a way of fixing that, like having a separate template_top file dedicated to the order handler without all the "muck".

 

The biggest challenge is compatibility with all shipping/payment/order total modules. But I think I'm well on the way to solving this.

A problem, of course, is that since (almost) nobody reports any mini bugs or problems, so then I have to assume that there aren't any. :rolleyes:

 

 

Like i mentioned the effort and support you put in your add-ons is exceptional and deserves respect.

 

Regarding Bug 4, very clever! Didn't think about it that way.

Bug 5, i'll check it out. It is not a super vital function though.

 

Thanks again and kind regards

Lambros

 

Glad I can be of help!

I'll take a look at the invoice bug and see if I can give you a quick fix for it.

Link to comment
Share on other sites

Bug 1:

Invoice function inside the footer menu outputs an empty invoice page.

No customer info, no order details (except for order number and date), just the stylesheet is loaded nothing else.

This doesn't happen if I click the jquery ui invoice button inside the order detail page where it loads the vanilla osC invoice.

Tested with all three browsers. Same result.

 

 

All right!

 

Solution: find this code in ./admin/includes/modules/order_handler/general_functions.php

      $script = tep_catalog_href_link($script, (is_numeric($version) ? $version : ''), $connection);
    else
      $script = (($cookieless == true) ? HTTP_COOKIELESS_DOMAIN : '') . $script;
    if ($type == 'script')
      return '<script src="' . (($cookieless == true) ? HTTP_COOKIELESS_DOMAIN : '') . $script . '"></script>';

    if ($type == 'link')
      return '<link rel="stylesheet" href="' . (($cookieless == true) ? HTTP_COOKIELESS_DOMAIN : '') . $script . '" />';

    return "No link found";
  }

Replace With

      $script = tep_catalog_href_link($script, (is_numeric($version) ? $version : ''), $connection);
    else
      $script = (($cookieless == true) ? HTTP_COOKIELESS_DOMAIN : '') . $script;
    if ($type == 'script')
      return '<script src="' . (($cookieless == true) ? HTTP_COOKIELESS_DOMAIN : '') . $script . '"></script>';

    if ($type == 'link')
      return '<link rel="stylesheet" href="' . (($cookieless == true) ? HTTP_COOKIELESS_DOMAIN : '') . $script . '" />';

    return "No link found";
  }

  function tep_address($address_format, $address, $html, $boln, $eoln, $skip_name = false) {
    $company = tep_output_string_protected($address['company']);
    if ( ! $skip_name ) {
      if (isset($address['firstname']) && tep_not_null($address['firstname'])) {
        $firstname = tep_output_string_protected($address['firstname']);
        $lastname = tep_output_string_protected($address['lastname']);
      } elseif (isset($address['name']) && tep_not_null($address['name'])) {
        $firstname = tep_output_string_protected($address['name']);
        $lastname = '';
      } else {
        $firstname = '';
        $lastname = '';
      }
    }
    $street = tep_output_string_protected($address['street_address']);
    $suburb = tep_output_string_protected($address['suburb']);
    $city = tep_output_string_protected($address['city']);
    $state = tep_output_string_protected($address['state']);
    if (isset($address['country_id']) && tep_not_null($address['country_id'])) {
      $country = tep_get_country_name($address['country_id']);

      if (isset($address['zone_id']) && tep_not_null($address['zone_id'])) {
        $state = tep_get_zone_code($address['country_id'], $address['zone_id'], $state);
      }
    } elseif (isset($address['country']) && tep_not_null($address['country'])) {
      $country = tep_output_string_protected($address['country']);
    } else {
      $country = '';
    }
    $postcode = tep_output_string_protected($address['postcode']);
    $zip = $postcode;

    if ($html) {
// HTML Mode
      $HR = '<hr />';
      $hr = '<hr />';
      if ( ($boln == '') && ($eoln == "\n") ) { // Values not specified, use rational defaults
        $CR = '<br />';
        $cr = '<br />';
        $eoln = $cr;
      } else { // Use values supplied
        $CR = $eoln . $boln;
        $cr = $CR;
      }
    } else {
// Text Mode
      $CR = $eoln;
      $cr = $CR;
      $HR = '----------------------------------------';
      $hr = '----------------------------------------';
    }

    $statecomma = '';
    $streets = $street;
    if ($suburb != '') $streets = $street . $cr . $suburb;
    if ($country == '') $country = tep_output_string_protected($address['country']);
    if ($state != '') $statecomma = $state . ', ';

    $fmt = $address_format;
    eval("\$address = \"$fmt\";");

    if ( (ACCOUNT_COMPANY == 'true') && (tep_not_null($company)) ) {
      $address = $company . $cr . $address;
    }

    return $address;
  }


Find this code

    echo tep_static_link( DIR_WS_MODULES . 'order_handler/css/01_03_bootstrap-multiselect-3.1.1.css', 'link', false, false, '0928' );
    echo tep_static_link( DIR_WS_MODULES . 'order_handler/css/01_04_bootstrap-theme-custom.css', 'link', false, false, '0928' );
    echo tep_static_link( DIR_WS_MODULES . 'order_handler/css/01_05_tikslusdialog.css', 'link', false, false, '0928' );
    echo tep_static_link( DIR_WS_MODULES . 'order_handler/css/01_06_select2.css', 'link', false, false, '0928' );
    echo tep_static_link( DIR_WS_MODULES . 'order_handler/css/01_07_red.css', 'link', false, false, '0928' );
    echo tep_static_link( DIR_WS_MODULES . 'order_handler/css/01_08_jquery.contextMenu.css', 'link', false, false, '0928' );
    echo tep_static_link( DIR_WS_MODULES . 'order_handler/css/01_09_orange.css', 'link', false, false, '0928' );
    echo tep_static_link( DIR_WS_MODULES . 'order_handler/css/01_10_jtable-custom.css', 'link', false, false, '0928' );
    echo tep_static_link( DIR_WS_MODULES . 'order_handler/css/01_11_contact.css', 'link', false, false, '0928' );
    echo tep_static_link( DIR_WS_MODULES . 'order_handler/css/01_12_jquery.qtip.css', 'link', false, false, '0928' );
    echo tep_static_link( DIR_WS_MODULES . 'order_handler/css/01_13_order_handler.css', 'link', false, false, '0928' );
  }

Replace With

    echo tep_static_link( DIR_WS_MODULES . 'order_handler/css/01_03_bootstrap-multiselect-3.1.1.css', 'link', false, false, '0928' );
    echo tep_static_link( DIR_WS_MODULES . 'order_handler/css/01_04_bootstrap-theme-custom.css', 'link', false, false, '0928' );
    echo tep_static_link( DIR_WS_MODULES . 'order_handler/css/01_05_tikslusdialog.css', 'link', false, false, '0928' );
    echo tep_static_link( DIR_WS_MODULES . 'order_handler/css/01_06_select2.css', 'link', false, false, '0928' );
    echo tep_static_link( DIR_WS_MODULES . 'order_handler/css/01_07_red.css', 'link', false, false, '0928' );
    echo tep_static_link( DIR_WS_MODULES . 'order_handler/css/01_08_jquery.contextMenu.css', 'link', false, false, '0928' );
    echo tep_static_link( DIR_WS_MODULES . 'order_handler/css/01_09_orange.css', 'link', false, false, '0928' );
    echo tep_static_link( DIR_WS_MODULES . 'order_handler/css/01_10_jtable-custom.css', 'link', false, false, '0928' );
    echo tep_static_link( DIR_WS_MODULES . 'order_handler/css/01_11_contact.css', 'link', false, false, '0928' );
    echo tep_static_link( DIR_WS_MODULES . 'order_handler/css/01_12_jquery.qtip.css', 'link', false, false, '0928' );
    echo tep_static_link( DIR_WS_MODULES . 'order_handler/css/01_13_order_handler.css', 'link', false, false, '0928' );
  }

////
//Error Handling
  function error_message( $title, $message ) {
    $error_message =
      '<script>
          window.opener.oHandler.gritter( "' . $title . '", "' . $message . '", "fa-warning" );
          self.close();
          $( ".progress" ).show();
        </script>';
    die( $error_message );
  }

Replace the entire ./admin/print_batch_process_3.php with this

<?php
/*
  Advanced Order Handler Rev3 for osCommerce 2.3.3
  Copyright (C) 2014  Jonas [email protected]

  This file is part of Advanced Order Handler Rev3.

  Advanced Order Handler Rev3 is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Advanced Order Handler Rev3 is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with Advanced Order Handler Rev3.  If not, see <http://www.gnu.org/licenses/>.
*/

	require 'includes/application_top.php';
	require_once DIR_WS_MODULES . 'order_handler/general_functions.php';

	require DIR_WS_CLASSES . 'currencies.php';
	$currencies 	 = new currencies();

	//Define Tables to Variables
	$tablename_o 	 = TABLE_ORDERS;
	$tablename_ot  = TABLE_ORDERS_TOTAL;
	$tablename_c 	 = TABLE_COUNTRIES;
	$tablename_osh = TABLE_ORDERS_STATUS_HISTORY;


	unset( $batch_order_numbers );
	if ( $_POST['batch_order_numbers'] ) {
		foreach ( $_POST['batch_order_numbers'] as $order_number => $print_order ) {
			$batch_order_numbers[] = $order_number;
		}
	}

	// Uncomment this if you always want your orders sorted by order number
	//sort( $batch_order_numbers );

	

	if ( !( is_array( $batch_order_numbers ) ) && !isset( $_POST['order_nums'] ) ) {
		error_message( "404: Not Found", "Error: no orders selected!" );
	} else if ( isset( $_POST['batch_delete_x'] ) || isset( $_POST['batch_delete_y'] ) ) {
			include DIR_WS_LANGUAGES . $language . '/order_handler.php';
			if ( isset( $_POST['batch_confirm_x'] ) || isset( $_POST['batch_confirm_y'] ) ) {
				die( include DIR_WS_MODULES . 'order_handler/oc_batch_delete.php' );
			} else {
				die( include DIR_WS_MODULES . 'order_handler/oc_batch_delete_confirm.php' );
			}
		}

	include DIR_WS_MODULES . 'order_handler/order_prepared.php';
	$order = new order;

	if ( 'envelope' == $_POST['target_file'] ) {
		require DIR_WS_MODULES . 'order_handler/print_batch_envelope.php';
	} elseif( 'export' == $_POST['target_file'] ) {

			#Export to XML
			header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
			header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
			header("Cache-Control: no-store, no-cache, must-revalidate");
			header("Cache-Control: post-check=0, pre-check=0", false);
			header("Pragma: no-cache");
			header('Content-Type: text/xml');
			header('Content-Disposition: attachment; filename="'.date('Ymdhis').'.xml"');
			$buffer[] = '<?xml version="1.0" encoding="utf-8"?>'."\r\n";
			$buffer[] = '<your_shipping_here>'."\r\n";
			foreach ($batch_order_numbers as $order_id) {
				if($order_id != "null")
				{
					$db_get = "SELECT o.orders_id,o.customers_id,o.customers_name,o.payment_method,o.delivery_name,o.delivery_company,o.delivery_street_address,o.delivery_suburb,o.delivery_city,o.delivery_postcode,o.delivery_state,o.delivery_country,o.customers_telephone,o.customers_email_address,ot.value,c.countries_iso_code_2 FROM $tablename_o o, $tablename_ot ot, $tablename_c c WHERE (o.orders_id = ot.orders_id AND ot.class = 'ot_total') AND (o.orders_id = ? AND o.delivery_country = c.countries_name) ORDER BY orders_id DESC LIMIT 1";
					$db_res = mysqli_prepared_query($db_get, "i", array($order_id));
					$db_ary = $db_res[0];
					$buffer[] = "\t".'<receiver rcvid="'.$db_ary['orders_id'].'">'."\r\n";
					$buffer[] = "\t\t".'<val n="name">'.$db_ary['delivery_name'].'</val>'."\r\n";
					$buffer[] = "\t\t".'<val n="address1">'.$db_ary['delivery_street_address'].'</val>'."\r\n";
					$buffer[] = "\t\t".'<val n="address2">'.$db_ary['delivery_suburb'].'</val>'."\r\n";
					$buffer[] = "\t\t".'<val n="zipcode">'.$db_ary['delivery_postcode'].'</val>'."\r\n";
					$buffer[] = "\t\t".'<val n="city">'.$db_ary['delivery_city'].'</val>'."\r\n";
					$buffer[] = "\t\t".'<val n="country">'.$db_ary['countries_iso_code_2'].'</val>'."\r\n";
					$buffer[] = "\t\t".'<val n="contact">'.$db_ary['customers_name'].'</val>'."\r\n";
					$buffer[] = "\t\t".'<val n="phone">'.$db_ary['customers_telephone'].'</val>'."\r\n";
					$buffer[] = "\t\t".'<val n="email">'.$db_ary['customers_email_address'].'</val>'."\r\n";
					$buffer[] = "\t\t".'<val n="sms">'.$db_ary['customers_telephone'].'</val>'."\r\n";
					$buffer[] = '</val>'."\r\n";
					$buffer[] = "\t\t".'</container>'."\r\n";
					$buffer[] = "\t".'</shipment>'."\r\n";
				}
			}
			$buffer[] = '</your_shipping_here>'."\r\n";
			die( implode('',$buffer) );

		} elseif ( $_POST['target_file'] == 'labels' ) {

		//Create CSV for Envelope
		header( 'Content-type: application/csv' );
		header( 'Content-Disposition: inline; filename="'.date( "ymd" ).'.csv"' );

		echo "Name;Street address;Postcode;City;Country\n";

			$order->prepare_query();

		foreach ( $batch_order_numbers as $order_id ) {
			$order->query( $order_id );

			echo "" . $order->delivery['name'] . ";" . $order->delivery['street_address'] . ";" . $order->delivery['postcode'] . ";" . $order->delivery['city'] . ";" . $order->delivery['country'] . "\n";
		}
			$order->close_query();
			exit;

		}

		$total_invoices = count( $batch_order_numbers );
	?>
			<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN">
			<html <?php echo HTML_PARAMS; ?>>
			<head>
				<meta http-equiv="Content-Type" content="text/html; charset=<?php echo CHARSET; ?>">
				<meta name="robots" content="noindex,nofollow">
				<title><?php echo 'Invoices (' . $total_invoices . ')'; ?></title>
				<link rel="stylesheet" type="text/css" href="includes/stylesheet.css">

				<script src="<?php echo tep_catalog_href_link( 'ext/jquery/jquery-2.1.0.min.js' ); ?>"></script>
			</head>
			<body marginwidth="0" marginheight="0" topmargin="0" bottommargin="0" leftmargin="0" rightmargin="0" bgcolor="#FFFFFF">
				<div id="ajax_loader" style="display:block;">
					<div id="loader_content">
						<div class="circle"></div>
						<div class="circle1"></div>

						<div class="progress progress-striped active">
							<div class="progress-bar" id="progress-bar"  role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%">
								<span class="" style="color: rgb(255, 255, 255);font-weight: 400;">0 Updated</span>
							</div>
						</div>
					</div>
				</div>
				<script>
					$( ".progress" ).show();
				</script>

				<?php
		$firstinvoice=1;
		$invoice = 0;

		$order->prepare_query();

		//Let's iterate the Invoices
		foreach ( $batch_order_numbers as $order_id ) {
			$order->query( $order_id );
			if ( $firstinvoice ) {
				$firstinvoice = 0;
			} else {
				echo '<br style="">';
			}

			$invoice++;
			include DIR_WS_MODULES . 'order_handler/print_batch_invoice.php';
		}

		$order->close_query();

	?>

				<script>
				$(function(){
					$( "#ajax_loader" ).remove();
					// Required for Print Page to work in Chrome
					<?php if ( "true" == $_POST['print'] ) { ?>
						// Comment/Uncomment /*window.print();*/ to display a print window after invoices has loaded.
						// Comment/Uncomment /*window.close();*/ to close window/tab after print window has closed.
						setTimeout( function(){ window.print(); /*window.close();*/ }, 1 );
					<?php } ?>
	    	});
				</script>
			</body>
			</html>
			<?php
	require DIR_WS_INCLUDES . 'application_bottom.php';

Link to comment
Share on other sites

Envelope Module - Non-mysqlnd compatible version

 

To add the Envelope Module to the non mysqlnd version of the Order Handler, make the following changes.

 

1. Make the replacement mentioned in post #204

 

2.  In ./admin/get_table.php

 

Find

    tep_load_css( true );

Replace With

    tep_load_css( false );

Find

    tep_preload_scripts( $store_country_iso_2[0]['countries_iso_code_2'], true );
    tep_afterload_scripts( true );

Replace With

    tep_preload_scripts( $store_country_iso_2[0]['countries_iso_code_2'], false );
    tep_afterload_scripts( false );

Find - Two occurrences - line 163 & 215

    tep_preload_scripts( $store_country_iso_2[0]['countries_iso_code_2'], true );

Replace With

    tep_preload_scripts( $store_country_iso_2[0]['countries_iso_code_2'], false );

3. In ./admin/includes/languages/english/order_handler.php

 

Find

@[member=definedmedia]('ENTRY_EXPORT', ' Export');

Replace With

@[member=definedmedia]('ENTRY_EXPORT', ' CSV');

4. In ./admin/includes/modules/order_handler/js/01_03_order_handler_rev3_module.js

 

Find & REMOVE

                form             = $( "#batch_orders" ),

Find

                    if ( target != "invoice.php" )
                        form[0].target = "_self";
                    else
                        newWin = window.open("","_newtab");

                    form.submit();
                }

Replace With

                    if ( target != "invoice" && "envelope" != target )
                        s.buttons.batchOrders[0].target = "_self";
                    else if ( "envelope" == target )
                        window.open( "", "_envelope" );
                    else
                        window.open( "", "_newtab" );

                    s.buttons.batchOrders.submit();
                }

Find

                if ( target != "invoice.php" )
                    navbar[0].target = "_self";

Replace With

                if ( target != "invoice" && "envelope" != target )
                    navbar[0].target = "_self";
                else if ( "envelope" == target )
                    navbar[0].target = "_envelope";

Find

            s.buttons.autostatus.multiselect({
                buttonClass: 'btn btn-lg btn-default'
            });

            s.buttons.autostatus.multiselect({
                buttonClass: 'btn btn-lg btn-default'
            });

Replace With

            s.buttons.autostatus.multiselect({
                buttonClass: 'btn btn-lg btn-default'
            });

            $( "#envelope_back" ).multiselect({
                buttonClass: 'btn btn-lg btn-default',
                maxHeight: 400,
                onChange: function(element, checked) {
                    this.$select.find( "option" ).removeAttr( "selected" );
                    element.attr( "selected", "selected" );

                    // Toggle State of Tooltips
                    localStorage.evelopeSize = element[0].value;
                    this.$select.multiselect('select', localStorage.evelopeSize);
                }
            });

5. In ./admin/includes/modules/order_handler/navbars.php

 

Replace Entire File

<?php
/*
  Advanced Order Handler Rev3 for osCommerce 2.3.3
  Copyright (C) 2014  Jonas [email protected]

  This file is part of Advanced Order Handler Rev3.

  Advanced Order Handler Rev3 is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Advanced Order Handler Rev3 is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with Advanced Order Handler Rev3.  If not, see <http://www.gnu.org/licenses/>.
*/
?>

<div id="navBars">
<!-- Top Navbar -->
<nav class="navbar navbar-default navbar-fixed-top navbar-inverse" role="navigation" data-tooltip-title="<?php echo TOOLTIP_NAVIGATION_TOP_TITLE; ?>" id="navigationTop" style="height: 44px;min-height: 44px;">
  <div class="navbarOverlay"></div>
  <div class="container-fluid">

    <!-- Expand Window Button -->
    <div class="input-group btn-group navbar-left" style="width:50px;" data-toggle="buttons">
      <i class="expand_table fa fa-arrows-alt tooltip_set" data-placement="bottom" title="<?php echo TOOLTIP_EXPAND_TABLE; ?>" style=""></i>
    </div>
    <!-- END Expand Window Button -->

    <!-- Toggle Right Click Menu Button -->
    <div class="input-group btn-group navbar-form navbar-left tooltip_set" data-placement="bottom" title="<?php echo TOOLTIP_ADMIN_MENU; ?>" style="width:50px;margin-left: 50px;">
      <input type="checkbox" class="js-switch" id="js-switch" checked />
    </div>
    <div class="input-group btn-group navbar-form navbar-left" style="width:14%;min-width:250px;" data-toggle="buttons">
      <input type="checkbox" name="context_menu" <?php echo (isset($_GET['context_menu']) ? 'checked="checked"' : ''); ?> id="context_menu" class="iCheck" checked="checked">
      <label for="context_menu"><?php echo TEXT_INFO_DISABLE_MENU; ?></label>
    </div>
    <!-- END Toggle Right Click Menu Button -->

    <!-- Search Orders -->
    <?php
    $orders_statuses = array();
    $envelope_back   = array();
    $orders_status_array = array();
    $orders_status_query = mysqli_prepared_query("SELECT orders_status_id, orders_status_name FROM $tablename_os WHERE language_id = ? ORDER BY orders_status_id ASC", "i", array($languages_id));
    foreach ($orders_status_query as $orders_status) {
      $orders_statuses[] = array('id' => $orders_status['orders_status_id'],
       'text' => $orders_status['orders_status_name']);
      $orders_status_array[$orders_status['orders_status_id']] = $orders_status['orders_status_name'];
    }

    # International standard sizes
    $envelope_back[] =
    array(
      'id'   => json_encode(array(110,220)),
      'text' => 'DL'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(81,114)),
      'text' => 'C7'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(81,162)),
      'text' => 'C7/C6'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(114,162)),
      'text' => 'C6'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(114,229)),
      'text' => 'C6/C5'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(162,229)),
      'text' => 'C5'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(229,324)),
      'text' => 'C4'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(324,458)),
      'text' => 'C3'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(125,176)),
      'text' => 'B6'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(176,250)),
      'text' => 'B5'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(250,353)),
      'text' => 'B4'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(280,400)),
      'text' => 'E4'
    );

    # North American sizes
    $envelope_back[] =
    array(
      'id'   => json_encode(array(111.1,146.1)),
      'text' => 'A2'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(120.7,165.1)),
      'text' => 'A6'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(133.4,184.2)),
      'text' => 'A7'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(139.7,206.4)),
      'text' => 'A8'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(146.1,222.3)),
      'text' => 'A9'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(152.4,241.3)),
      'text' => 'A10'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(92.1,165.1)),
      'text' => '6¾'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(98.4,190.5)),
      'text' => '7¾'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(98.4,225.4)),
      'text' => '9'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(104.8,241.3)),
      'text' => '10'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(114.3,263.5)),
      'text' => '11'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(120.7,279.4)),
      'text' => '12'
    );
    $envelope_back[] =
    array(
      'id'   => json_encode(array(127.0,292.1)),
      'text' => '14'
    );


    echo
    tep_draw_form('search', "get_table.php", '', 'get', 'class="navbar-form" role="search"') .
      '<div class="form-group navbar-left navbar-form" style="width: 23%;">' .
        tep_draw_input_field('search', '', 'class="bigdrop tooltip_set" placeholder="Search by Name, E-Mail, Order Number, Customer Number" title="' . TOOLTIP_SEARCH_ORDERS . '" id="search_orders" style="min-width:335px;"') .
      '</div>' .
    '</form>'; ?>
    <!-- END Search Orders -->

    <!-- Remove Gritter Button -->
    <div class="navbar-form input-group btn-group navbar-right" style="margin-right: -15px;" data-toggle="buttons">
      <label class="btn btn-lg btn-danger" style="opacity:0;padding: 0px 5px 0px 5px;z-index:100;margin-right:20px;" id="gritterRemove">
        <i class="fa fa-times fa-2 tooltip_set" data-placement="top" title="<?php echo TOOLTIP_GRITTER_REMOVE; ?>" style="width: 20px;font-size:1.5em;z-index:0;"></i>
        <?php echo tep_draw_checkbox_field('long_poller'); ?>
      </label>
    </div>
    <!-- END Remove Gritter Button -->

    <!-- Refresh Button -->
    <div class="navbar-form input-group btn-group navbar-right" style="" data-toggle="buttons">
      <label class="btn btn-lg btn-success" style="padding: 0px 5px 0px 5px;z-index:100;" id="refresh-button">
        <i class="fa fa-2 fa-refresh tooltip_set" data-placement="bottom" title="<?php echo TOOLTIP_REFRESH; ?>" style="font-size:1.5em;z-index:0;">
          <a href="<?php echo tep_href_link(FILENAME_LOGIN, 'action=logoff'); ?>"></a>
        </i>
      </label>
    </div>
    <!-- END Refresh Button -->

    <!-- Logout Button -->
    <div class="navbar-form input-group btn-group navbar-right" style="" data-toggle="buttons">
      <label class="btn btn-lg btn-danger" style="padding: 0px 5px 0px 5px;z-index:100;margin-right:20px;" id="logout-button">
        <i class="fa fa-2 fa-sign-out tooltip_set" data-placement="bottom" title="<?php echo TOOLTIP_LOGOUT; ?>" style="width: 20px;font-size:1.5em;z-index:0;">
          <a href="<?php echo tep_href_link(FILENAME_LOGIN, 'action=logoff'); ?>"></a>
        </i>
      </label>
    </div>
    <!-- END Logout Button -->

    <!-- Sort By Status -->
    <?php
    echo
    tep_draw_form('status', "get_table.php", '', 'get', 'id="select_status"') .
      '<div class="form-group navbar-right navbar-form tooltip_set" data-placement="bottom" title="' . TOOLTIP_SORT_STATUS . '" style="width:20%;min-width:335px;margin-right:0;">' .
        tep_draw_pull_down_menu('status', array_merge(array(array('id' => '', 'text' => TEXT_ALL_ORDERS)), $orders_statuses), '', 'id="status" style="width:100%;"') .
      '</div>' .
    '</form>'; ?>
    <!-- END Sort By Status -->

  </div>
</nav>
<!-- END Top Navbar -->


<!-- Bottom Navbar -->
<?php
echo tep_draw_form('navbar', 'print_batch_process_3.php', '', 'post', 'target="_newtab" id="navbar"') . tep_draw_hidden_field('print', "true", 'id="print"'); ?>

  <nav class="navbar navbar-default navbar-fixed-bottom navbar-inverse" role="navigation" id="navigationBottom" data-tooltip-title="<?php echo TOOLTIP_NAVIGATION_BOTTOM_TITLE; ?>">
    <div class="navbarOverlay"></div>

    <!-- Navbar Header -->
    <div class="navbar-header">
      <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"></button>
      <a class="navbar-brand" href="javascript:void(0);">Menu</a>
    </div>
    <!-- END Navbar Header -->

    <!-- Output Menu -->
    <div class="container-fluid">
      <div class="btn-group navbar-form navbar-left" id="target_file" style="width:28%;min-width:446px;padding:0;" data-toggle="buttons">
        <label class="btn btn-lg btn-primary active">
          <?php echo tep_draw_radio_field('target_file', 'invoice', true) . ENTRY_INVOICE; ?>
        </label>
        <label class="btn btn-lg btn-primary">
          <?php echo tep_draw_radio_field('target_file', 'labels') . ENTRY_EXPORT; ?>
        </label>
        <label class="btn btn-lg btn-primary">
          <?php echo tep_draw_radio_field('target_file', 'export') . ENTRY_XML; ?>
        </label>
        <label class="btn btn-lg btn-primary">
          <?php echo tep_draw_radio_field('target_file', 'envelope') . ENTRY_ENVELOPE; ?>
        </label>
        <?php echo tep_draw_pull_down_menu('envelope_back', $envelope_back, json_encode(array(162,229)), 'id="envelope_back" class="multiselect btn btn-lg btn-info"'); ?>
      </div>
      <!-- END Output Menu -->

      <!-- Email Customer Menu -->
      <div class="input-group navbar-form btn-group navbar-left" style="margin-left:20px;margin-right:20px;width:10%;min-width:160px;padding:0;" data-toggle="buttons">
        <span class="input-group-addon"><b class="glyphicon glyphicon-envelope"></b></span>
        <label class="btn btn-lg btn-info tooltip_set" data-placement="top" title="<?php echo IMAGE_SEND_EMAIL; ?>">
          <?php echo ENTRY_NOTIFY_YES . tep_draw_radio_field('notify', 'Yes'); ?>
        </label>
        <label class="btn btn-lg btn-info active tooltip_set" data-placement="top" title="<?php echo TOOLTIP_DO_NOT_SEND_EMAIL; ?>" style="border-bottom-right-radius:4px;border-top-right-radius:4px;">
          <?php echo tep_draw_radio_field('notify', 'No', true) . ENTRY_NOTIFY_NO; ?>
        </label>
      </div>
      <!-- END Email Customer Menu -->

      <!-- Update Status Menu -->
      <div class="input-group btn-group navbar-form navbar-left" id="autoupdatestatus" style="min-width:400px;width:29%;padding:0;" data-toggle="buttons">
        <span class="input-group-addon"><b class="glyphicon"></b><?php echo ENTRY_UPDATE_STATUS; ?></span>
        <label class="btn btn-lg btn-info tooltip_set" data-placement="top" title="<?php echo TOOLTIP_UPDATE_STATUS; ?>">
          <?php echo ENTRY_NOTIFY_YES . tep_draw_radio_field('autoupdatestatus', 'Yes'); ?>
        </label>
        <label class="btn btn-lg btn-info active tooltip_set" data-placement="top" title="<?php echo TOOLTIP_DO_NOT_UPDATE_STATUS; ?>">
          <?php echo tep_draw_radio_field('autoupdatestatus', 'No', true) . ENTRY_NOTIFY_NO; ?>
        </label>
        <?php echo tep_draw_pull_down_menu('autostatus', $orders_statuses, 3, 'id="autostatus" class="multiselect btn btn-lg btn-info"'); ?>
      </div>
      <!-- END Update Status Menu -->

      

      

      <!-- Delete Button -->
      <div class="btn-group navbar-form navbar-right" style="margin-right: -15px;">
        <button type="button" id="batch_delete" name="batch_delete" class="btn btn-lg btn-danger navbar-right ui-priority-secondary tooltip_set" data-placement="top" title="<?php echo TOOLTIP_DELETE_ORDERS; ?>"><?php echo IMAGE_DELETE; ?></button>
      </div>
      <!-- END Delete Button -->

      <!-- Ajax Polling Menu -->
      <div class="input-group input-group-lg btn-group navbar-form navbar-right" style="width:12%;min-width:200px;padding:0;margin-left:10px;margin-right:10px;" id="poller" data-toggle="buttons">
        <span class="input-group-addon"><i class="fa fa-cog"></i></span>
          <?php echo tep_draw_input_field('poll_timer', '20', 'id="poll_timer" class="form-control tooltip_set" data-placement="top" title="' . TOOLTIP_TIMEOUT . '" placeholder="20" style="width:55px;height:46px;"'); ?>

        <label class="btn btn-lg btn-default" style="padding: 9px 16px 8px 10px;" id="enable_button">
          <i class="fa fa-check fa-2 tooltip_set" data-placement="top" title="<?php echo TOOLTIP_ENABLE_POLLING; ?>" style="width: 20px;font-size:1.5em;"></i>
          <?php echo tep_draw_checkbox_field('long_poller'); ?>
        </label>

        <label class="btn btn-lg btn-danger active" style="padding: 9px 14px 8px 12px;z-index:100;" id="disable_button">
          <i class="fa fa-times fa-2 tooltip_set" data-placement="top" title="<?php echo TOOLTIP_DISABLE_POLLING; ?>" style="width: 20px;font-size:1.5em;z-index:0;"></i>
          <?php echo tep_draw_checkbox_field('long_poller'); ?>
        </label>
      </div>
      <!-- END Ajax Polling Menu -->

      <!-- Confirm Button -->
      <div class="btn-group navbar-form navbar-right" style="margin-right:0;padding:0;margin-right:10px;">
        <a href="<?php echo tep_href_link("get_table.php", tep_get_all_get_params(array('action')) . 'action=update_orders_status'); ?>" target="_newtab" type="submit" class="submit_button btn btn-lg btn-success ui-priority-primary" style="font-weight: normal;"><?php echo IMAGE_CONFIRM; ?></a>
      </div>
      <!-- END Confirm Button -->

    </div>
  </nav>
</form>
</div>

6.  Copy new files from Package uploaded on 16 Jul 2014

 

  1. ./admin/includes/modules/order_handler/css/images/airmail-graphicsfairy002c-right.jpg
  2. ./admin/includes/modules/order_handler/font/*
  3. ./admin/includes/modules/order_handler/print_batch_envelope.php
  4. ./admin/includes/modules/order_handler/tfpdf.php
Link to comment
Share on other sites

Hi Jonas

 

I have to say that I disagree, since when did commercial products outperform Open Source projects?  :P
I can't say that I'm very impressed with most of the commercial tools I use in my business, even though there are exceptions of course. They simply lack creativity and only have the absolutely base functionality which often leaves a lot to be desired ..

 

Well they might lack the creativity, true, but since you pay for them you have the rights to get a 100% working tool even if it is just basic. If not, then that would be unprofessional work by the seller/developer.

Of course open source is awesome if you have good people working on it (like yourself for example). But since it is free we can't always expect it to work 100% out of the box. That is why we have this nice forum here to do that together. (In this case it is only you though... :P  ) Some people just always think that osC and it's add-ons should work perfectly from the start without any efforts and that is wrong thinking. If it works 100% great! If not then pull up your sleeves and get to it.... :D

 

 

I'm certain that there can be a 100% working order editor which fits all needs. This, of course, requires that there aren't any prior errors on the osC installation when the order editor is installed. It looks like a lot of people have broken/deprecated Javascript code from other Add-Ons which can and probably will break the order handler.

 

Yep. some people install many add-ons that conflict later on and then the point the finger mostly to the latest installed one and forget that the previous installed add-ons could be faulty too.

 

Your fix and additions from post 204 and 205 worked! Very nice! Now i just need to change the button size from btn-lg to maybe btn-sm or even btn-xs the footer menu getting quite full now.

On my 21" monitor no preblem but on my 15" notebook the footer menu covers the under part of the listing because of the btn-lg buttons. This is a part where everyone needs to adjust to their own needs.

 

Thanks again...Dr.

Kind regards

Lambros

 

Link to comment
Share on other sites

Zu früh gefreut!

 

I have the order handler on two shops installed both 2.3.4 untouched pure vanilla osC shops. one localhost and one real server.

On my localhost the patches/fixes that you posted in 204 and 205 worked. I can see the invoice and envelope now.

 

On my server i can't get the envelope to show. The invoice works though.

I get the following message from adobe reader: "This PDF document might not be displayed correctly."

Edited by Tsimi
Link to comment
Share on other sites

Zu früh gefreut!

 

I have the order handler on two shops installed both 2.3.4 untouched pure vanilla osC shops. one localhost and one real server.

On my localhost the patches/fixes that you posted in 204 and 205 worked. I can see the invoice and envelope now.

 

On my server i can't get the envelope to show. The invoice works though.

I get the following message from adobe reader: "This PDF document might not be displayed correctly."

 

Super schön!

 

I'm guessing this problem only occurs in Firefox, right?

 

I know I had this problem at some point when developing the envelope module but I can't remember why or how I fixed it. Do you get the same error if you use another envelope size and/or another address?

 

Any warnings or errors in your PHP log?

 

You can change a FPDF setting in ./admin/includes/modules/order_handler/print_batch_envelope.php which will instead of displaying the envelope in the browser force a download of the PDF.

 

I had problem printing the invoices from the built in PDF reader in Firefox so personally I always download and open them with Preview (PDF Reader software for Mac).

 

To do this change the second parameter ( 'I' ) on this line to e.g. 'D' instead.

  $pdf->Output( "Envelope-" . date( "ymd" ).'.pdf', 'I' );

From the Manual:

Destination where to send the document. It can take one of the following values:

  • I: send the file inline to the browser. The plug-in is used if available. The name given by name is used when one selects the "Save as" option on the link generating the PDF.
  • D: send to the browser and force a file download with the name given by name.
  • F: save to a local file with the name given by name (may include a path).
  • S: return the document as a string. name is ignored.

It could be a problem with your plugin/Browser Add-On. Found some threads about the problem on mozilla support forum:

https://support.mozilla.org/sv/questions/947783

https://support.mozilla.org/sv/questions/948061

Link to comment
Share on other sites

Hi Jonas

 

Tried with Chrome and IE too.

Chrome gives me same result invoice OK but error on the envelopes.

IE this one is the worst of all (of course, what else...) it even doesn't load the invoice nor the envelops in fact nothing from the footer menu but that is OK i don't really care about IE since i never use that to work in the admin area.

The funny thing is the right click function works perfect on IE but fails with Chrome or FF. What a crazy world... (w00t)

 

I will give it another go and report back...

 

Thank you .

 

Kind regards

Lambros

Link to comment
Share on other sites

Hi Jonas

 

Tried with Chrome and IE too.

Chrome gives me same result invoice OK but error on the envelopes.

IE this one is the worst of all (of course, what else...) it even doesn't load the invoice nor the envelops in fact nothing from the footer menu but that is OK i don't really care about IE since i never use that to work in the admin area.

The funny thing is the right click function works perfect on IE but fails with Chrome or FF. What a crazy world... (w00t)

 

I will give it another go and report back...

 

Thank you .

 

Kind regards

Lambros

 

Yeah, it's almost a harassment to work with IE. :x

 

Did you check your error log?

Since you're not having the same problem on your computer then perhaps your server configuration is set to add some kind of header to all pages.

 

Check the Request and Response Headers when opening envelopes and compare them to the ones your getting on localhost.

 

In Chrome developer tools you'll find these under the Network tab. First open some envelopes, go to that tab and open developer tools and click on the network tab, then reload the page and look at the print_batch_process_3.php request.

 

Under Response Headers you should find Content-Type:application/pdf

Link to comment
Share on other sites

  • 4 weeks later...

All right then, I have made an update to the Order Handler which should make it more stable and even more featureful than before!

 

Automatic Module Recalculation of Order Totals

Now when you modify an order the Order Handler will reload all shipping, payment and order total modules and recalculate Order Totals correctly.

E.g. if you change the Shipping Method for an order to 'Per Item' Shipping Method and then change the product quantity, then the order handler will Update the order to the correct Shipping Price.

 

Changing the Tax Rate etc. should now work without any miscalculations. At least, let's hope so!? :thumbsup:

 

Order Status Dropdown

Quickly change the Order Status without the need to go to any of the two 'Edit Order' Modes.

 

 

Shipping Method Dropdown

Quickly Change the Shipping Method on orders, the Order Handler will recalculate the correct shipping price and modify Order Totals.

 

 

Payment Method Dropdown

Quickly Change the Payment Method on orders, the Order Handler will recalculate the correct payment method price and modify Order Totals.

 

 

New Toolbar Button - Quick Search

Click on the magnifying glass and you will get a list with all orders from that customer.

 

 

Bug Fixes

All bugs mentioned so far should now be fixed and others as well..

 

 

I will Post the required changes here tomorrow and upload an updated package when I have time for that.

 

 

** See Screenshot Below **

 

 

 

Link to comment
Share on other sites

Just a quick mention the latest zip when unpacking also had problems it unpacked but with errors I found I was missing mainly images (bootstrap icons) and  a few files from the includes/modules/order_handler/  I compared to original download not quite sure what you removed to make space tested 7zip and winrar  to unpack.

 

So still a problem with the file size have not had time to test but the next few days

 

Regards

John

Edited by joli1811
To improve is to change; to be perfect is to change often.

 

Link to comment
Share on other sites

Just a quick mention the latest zip when unpacking also had problems it unpacked but with errors I found I was missing mainly images (bootstrap icons) and  a few files from the includes/modules/order_handler/  I compared to original download not quite sure what you removed to make space tested 7zip and winrar  to unpack.

 

So still a problem with the file size have not had time to test but the next few days

 

Regards

John

 

Hello John,

 

Let's put an end to the xz compression dilemma.

 

I will exclude the screenshots in any new packages that I upload and just compress them with Tar/Gzip instead. ;)

Link to comment
Share on other sites

Okay, so if you want to test this new update then make the following changes.

 

 

 

1. Find this in ./admin/get_table.php

     case "update_order":
    die( $orders_ajax->update_order( $_GET, $_POST ) );

Replace With

     case "update_order":
    die( $orders_ajax->update_order( $_GET, $_POST ) );
    case "update_shipping_method":
    die( $orders_ajax->update_shipping_method( $_GET ) );

______________________________________________________

 

 

Find This

     tep_load_css( true );

Replace With

     tep_load_css( false );

______________________________________________________

 

Find This

     tep_preload_scripts( $store_country_iso_2[0]['countries_iso_code_2'], true );
    tep_afterload_scripts( true );

Replace With

     tep_preload_scripts( $store_country_iso_2[0]['countries_iso_code_2'], false );
    tep_afterload_scripts( false );

______________________________________________________

 

Find This

     tep_preload_scripts( $store_country_iso_2[0]['countries_iso_code_2'], true );

Replace With

     tep_preload_scripts( $store_country_iso_2[0]['countries_iso_code_2'], false );

______________________________________________________

 

Find This

     tep_afterload_scripts( true );

Replace With

     tep_afterload_scripts( false );

______________________________________________________

 

2. Add this to ./admin/includes/languages/english/order_handler.php

@[member=definedmedia]('TOOLTIP_SEARCH_CUSTOMER_ORDERS', 'List all orders from %s.');

______________________________________________________

 

3. Find this in ./admin/includes/modules/order_handler/css/01_10_jtable-custom.css

    div.jtable-main-container div.jtable-column-selection-container {

Replace With

    div.jtable-column-selection-container {

______________________________________________________

 

Find This

    div.jtable-main-container div.jtable-column-selection-container ul.jtable-column-select-list {

Replace With

    div.jtable-column-selection-container ul.jtable-column-select-list {

______________________________________________________

 

Find This

    div.jtable-main-container div.jtable-column-selection-container ul.jtable-column-select-list li {

Replace With

    div.jtable-column-selection-container ul.jtable-column-select-list li {

______________________________________________________

 

Find This

    div.jtable-main-container div.jtable-column-selection-container ul.jtable-column-select-list li label span {

Replace With

    div.jtable-column-selection-container ul.jtable-column-select-list li label span {

______________________________________________________

 

Find This

    div.jtable-main-container div.jtable-column-selection-container ul.jtable-column-select-list li input[type="checkbox"] {

Replace With

    div.jtable-column-selection-container ul.jtable-column-select-list li input[type="checkbox"] {

______________________________________________________

 

4. Find this in ./admin/includes/modules/order_handler/css/01_13_order_handler.css

    /* Body Prepare */
    #body_content.ready {
    width: 100%;
    position: absolute;

Replace With

    /* Body Prepare */
    #body_content.ready {
    width: 100%;
    position: absolute;
    padding-bottom: 50px;

______________________________________________________

 

5. Find this in ./admin/includes/modules/order_handler/general_functions.php

     $script = tep_catalog_href_link($script, (is_numeric($version) ? $version : ''), $connection);
    else
    $script = (($cookieless == true) ? HTTP_COOKIELESS_DOMAIN : '') . $script;
    if ($type == 'script')
    return '<script src="' . (($cookieless == true) ? HTTP_COOKIELESS_DOMAIN : '') . $script . '"></script>';
     
    if ($type == 'link')
    return '<link rel="stylesheet" href="' . (($cookieless == true) ? HTTP_COOKIELESS_DOMAIN : '') . $script . '" />';
     
    return "No link found";
    }
     

Replace With

     $script = tep_catalog_href_link($script, (is_numeric($version) ? $version : ''), $connection);
    else
    $script = (($cookieless == true) ? HTTP_COOKIELESS_DOMAIN : '') . $script;
    if ($type == 'script')
    return '<script src="' . (($cookieless == true) ? HTTP_COOKIELESS_DOMAIN : '') . $script . '"></script>';
     
    if ($type == 'link')
    return '<link rel="stylesheet" href="' . (($cookieless == true) ? HTTP_COOKIELESS_DOMAIN : '') . $script . '" />';
     
    return "No link found";
    }
     
    function tep_address($address_format, $address, $html, $boln, $eoln, $skip_name = false) {
    $company = tep_output_string_protected($address['company']);
    if ( ! $skip_name ) {
    if (isset($address['firstname']) && tep_not_null($address['firstname'])) {
    $firstname = tep_output_string_protected($address['firstname']);
    $lastname = tep_output_string_protected($address['lastname']);
    } elseif (isset($address['name']) && tep_not_null($address['name'])) {
    $firstname = tep_output_string_protected($address['name']);
    $lastname = '';
    } else {
    $firstname = '';
    $lastname = '';
    }
    }
    $street = tep_output_string_protected($address['street_address']);
    $suburb = tep_output_string_protected($address['suburb']);
    $city = tep_output_string_protected($address['city']);
    $state = tep_output_string_protected($address['state']);
    if (isset($address['country_id']) && tep_not_null($address['country_id'])) {
    $country = tep_get_country_name($address['country_id']);
     
    if (isset($address['zone_id']) && tep_not_null($address['zone_id'])) {
    $state = tep_get_zone_code($address['country_id'], $address['zone_id'], $state);
    }
    } elseif (isset($address['country']) && tep_not_null($address['country'])) {
    $country = tep_output_string_protected($address['country']);
    } else {
    $country = '';
    }
    $postcode = tep_output_string_protected($address['postcode']);
    $zip = $postcode;
     
    if ($html) {
    // HTML Mode
    $HR = '<hr />';
    $hr = '<hr />';
    if ( ($boln == '') && ($eoln == "\n") ) { // Values not specified, use rational defaults
    $CR = '<br />';
    $cr = '<br />';
    $eoln = $cr;
    } else { // Use values supplied
    $CR = $eoln . $boln;
    $cr = $CR;
    }
    } else {
    // Text Mode
    $CR = $eoln;
    $cr = $CR;
    $HR = '----------------------------------------';
    $hr = '----------------------------------------';
    }
     
    $statecomma = '';
    $streets = $street;
    if ($suburb != '') $streets = $street . $cr . $suburb;
    if ($country == '') $country = tep_output_string_protected($address['country']);
    if ($state != '') $statecomma = $state . ', ';
     
    $fmt = $address_format;
    eval("\$address = \"$fmt\";");
     
    if ( (ACCOUNT_COMPANY == 'true') && (tep_not_null($company)) ) {
    $address = $company . $cr . $address;
    }
     
    return $address;
    }
     

______________________________________________________

 

Find This

     $rs->data_seek( 0 );
    while ( $row = $rs->fetch_assoc() ) {
    $var[] = $row;
    }
     
    return substr( json_encode( $var ), 1, -1 );
     
    }
     
    ////
    // Returns a jTable formatted pull down menu with payment methods
      function tep_cfg_pull_down_payment( $selected_payment_module = '', $parameters = '', $custom = false, $required = false ) {
        global $language;
     
     

Replace With

     $rs->data_seek( 0 );
    while ( $row = $rs->fetch_assoc() ) {
    $var[] = $row;
    }
     
    return substr( json_encode( $var ), 1, -1 );
     
    }
     
    ////
    // Returns a jTable formatted pull down menu with shipping methods
    function tep_cfg_pull_down_shipping( $selected_shipping_module = '', $parameters = '', $custom = false, $required = false ) {
    global $language, $order;
     
    if ( defined( 'MODULE_SHIPPING_INSTALLED' ) && tep_not_null( MODULE_SHIPPING_INSTALLED ) ) {
    $installed_modules = explode( ';', MODULE_SHIPPING_INSTALLED );
     
    $include_modules = array();
    $modules = array();
     
    reset( $modules );
    while ( list( , $value ) = each( $installed_modules ) ) {
    $class = substr( $value, 0, strrpos( $value, '.' ) );
    $include_modules[] = array( 'class' => $class, 'file' => $value );
    }
     
    for ( $i=0, $n=sizeof( $include_modules ); $i<$n; $i++ ) {
    include_once DIR_FS_CATALOG . DIR_WS_LANGUAGES . $language . '/modules/shipping/' . $include_modules[$i]['file'];
    include_once DIR_FS_CATALOG . DIR_WS_MODULES . 'shipping/' . $include_modules[$i]['file'];
     
    $modules[] = new $include_modules[$i]['class'];
    }
    }
     
    $shipping_array = array();
    $default = '';
    if ( true === $custom ) {
    $shipping_array[] =
    array(
    'id' => json_encode( array( "_custom", $selected_shipping_module ) ),
    'text' => $selected_shipping_module,
    );
    }
     
    foreach ( $modules as $value ) {
    $shipping_array[] =
    array(
    'id' => $value->code,
    'text' => $value->title,
    );
     
    $haystack = preg_replace( '/\s\(.+$/', '', $selected_shipping_module );
    if ( stristr( $haystack, $value->title ) ) {
    $default = $value->code;
    }
    }
    //var_dump($default);
     
    return tep_draw_pull_down_menu( 'shipping_method_selection', $shipping_array, $default, $parameters, $required );
    }
     
    ////
    // Returns a jTable formatted pull down menu with payment methods
    function tep_cfg_pull_down_payment( $selected_payment_module = '', $parameters = '', $custom = false, $required = false ) {
    global $language, $order;
     

______________________________________________________

* End Part 1 *

 

______________________________________________________

 

 

 

 

Link to comment
Share on other sites

P.S. Sorry for the lost indentation in the previous post, the original post was too big and osc forum doesn't save indentation for some reason.. D.S.

 

Replace ./admin/includes/modules/order_handler/js/01_02_jquery.jtable.js With This: (1/3 Part of this file)

/* 

jTable 2.4.0
http://www.jtable.org

---------------------------------------------------------------------------

Copyright (C) 2011-2014 by Halil İbrahim Kalkan (http://www.halilibrahimkalkan.com)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

*/

/************************************************************************
* CORE jTable module                                                    *
*************************************************************************/
(function ($) {

    var unloadingPage;
    
    $(window).on('beforeunload', function () {
        unloadingPage = true;
    });
    $(window).on('unload', function () {
        unloadingPage = false;
    });

    $.widget("hik.jtable", {

        /************************************************************************
        * DEFAULT OPTIONS / EVENTS                                              *
        *************************************************************************/
        options: {

            //Options
            actions: {},
            fields: {},
            animationsEnabled: true,
            defaultDateFormat: 'yy-mm-dd',
            dialogShowEffect: 'fade',
            dialogHideEffect: 'fade',
            showCloseButton: false,
            loadingAnimationDelay: 500,
            saveUserPreferences: true,
            jqueryuiTheme: false,
            unAuthorizedRequestRedirectUrl: null,

            ajaxSettings: {
                type: 'POST',
                dataType: 'json',
                global: false
            },

            toolbar: {
                hoverAnimation: true,
                hoverAnimationDuration: 60,
                hoverAnimationEasing: undefined,
                items: []
            },

            //Events
            closeRequested: function (event, data) { },
            formCreated: function (event, data) { },
            formSubmitting: function (event, data) { },
            formClosed: function (event, data) { },
            loadingRecords: function (event, data) { },
            recordsLoaded: function (event, data) { },
            rowInserted: function (event, data) { },
            rowsRemoved: function (event, data) { },

            //Localization
            messages: {
                serverCommunicationError: 'An error occured while communicating to the server.',
                loadingMessage: 'Loading records...',
                loadingProgress: 'Records Loaded: {0} out of {1}',
                noDataAvailable: 'No data available!',
                areYouSure: 'Are you sure?',
                save: 'Save',
                saving: 'Saving',
                cancel: 'Cancel',
                error: 'Error',
                close: 'Close',
                cannotLoadOptionsFor: 'Can not load options for field {0}'
            }
        },

        /************************************************************************
        * PRIVATE FIELDS                                                        *
        *************************************************************************/

        _$mainContainer: null, //Reference to the main container of all elements that are created by this plug-in (jQuery object)

        _$titleDiv: null, //Reference to the title div (jQuery object)
        _$toolbarDiv: null, //Reference to the toolbar div (jQuery object)

        _$table: null, //Reference to the main <table> (jQuery object)
        _$tableBody: null, //Reference to <body> in the table (jQuery object)
        _$tableRows: null, //Array of all <tr> in the table (except "no data" row) (jQuery object array)

        _$busyDiv: null, //Reference to the div that is used to block UI while busy (jQuery object)
        _$busyMessageDiv: null, //Reference to the div that is used to show some message when UI is blocked (jQuery object)
        _$errorDialogDiv: null, //Reference to the error dialog div (jQuery object)

        _columnList: null, //Name of all data columns in the table (select column and command columns are not included) (string array)
        _fieldList: null, //Name of all fields of a record (defined in fields option) (string array)
        _keyField: null, //Name of the key field of a record (that is defined as 'key: true' in the fields option) (string)

        _firstDataColumnOffset: 0, //Start index of first record field in table columns (some columns can be placed before first data column, such as select checkbox column) (integer)
        _lastPostData: null, //Last posted data on load method (object)

        _cache: null, //General purpose cache dictionary (object)

        /************************************************************************
        * CONSTRUCTOR AND INITIALIZATION METHODS                                *
        *************************************************************************/

        /* Contructor.
        *************************************************************************/
        _create: function () {

            //Initialization
            this._normalizeFieldsOptions();
            this._initializeFields();
            this._createFieldAndColumnList();

            //Creating DOM elements
            this._createMainContainer();
            this._createTableTitle();
            this._createToolBar();
            this._createTable();
            this._createBusyPanel();
            this._createErrorDialogDiv();
            this._addNoDataRow();

            this._cookieKeyPrefix = this._generateCookieKeyPrefix();            
        },

        /* Normalizes some options for all fields (sets default values).
        *************************************************************************/
        _normalizeFieldsOptions: function () {
            var self = this;
            $.each(self.options.fields, function (fieldName, props) {
                self._normalizeFieldOptions(fieldName, props);
            });
        },

        /* Normalizes some options for a field (sets default values).
        *************************************************************************/
        _normalizeFieldOptions: function (fieldName, props) {
            if (props.listClass == undefined) {
                props.listClass = '';
            }
            if (props.inputClass == undefined) {
                props.inputClass = '';
            }

            //Convert dependsOn to array if it's a comma seperated lists
            if (props.dependsOn && $.type(props.dependsOn) === 'string') {
                var dependsOnArray = props.dependsOn.split(',');
                props.dependsOn = [];
                for (var i = 0; i < dependsOnArray.length; i++) {
                    props.dependsOn.push($.trim(dependsOnArray[i]));
                }
            }
        },

        /* Intializes some private variables.
        *************************************************************************/
        _initializeFields: function () {
            this._lastPostData = {};
            this._$tableRows = [];
            this._columnList = [];
            this._fieldList = [];
            this._cache = [];
        },

        /* Fills _fieldList, _columnList arrays and sets _keyField variable.
        *************************************************************************/
        _createFieldAndColumnList: function () {
            var self = this;

            $.each(self.options.fields, function (name, props) {

                //Add field to the field list
                self._fieldList.push(name);

                //Check if this field is the key field
                if (props.key == true) {
                    self._keyField = name;
                }

                //Add field to column list if it is shown in the table
                if (props.list != false && props.type != 'hidden') {
                    self._columnList.push(name);
                }
            });
        },

        /* Creates the main container div.
        *************************************************************************/
        _createMainContainer: function () {
            this._$mainContainer = $('<div />')
                .addClass('jtable-main-container')
                .appendTo(this.element);

            this._jqueryuiThemeAddClass(this._$mainContainer, 'ui-widget');
        },

        /* Creates title of the table if a title supplied in options.
        *************************************************************************/
        _createTableTitle: function () {
            var self = this;

            if (!self.options.title) {
                return;
            }

            var $titleDiv = $('<div />')
                .addClass('jtable-title')
                .appendTo(self._$mainContainer);

            self._jqueryuiThemeAddClass($titleDiv, 'ui-widget-header');

            $('<div />')
                .addClass('jtable-title-text')
                .appendTo($titleDiv)
                .append(self.options.title);

            var $titleDivNext = $('<div />')
                .addClass('jtable-title-text-next')
                .appendTo($titleDiv);

            if (self.options.showCloseButton) {

                var $textSpan = $('<span />')
                    .html(self.options.messages.close);

                $('<button></button>')
                    .addClass('jtable-command-button jtable-close-button')
                    .attr('title', self.options.messages.close)
                    .append($textSpan)
                    .appendTo($titleDivNext)
                    .click(function (e) {
                        e.preventDefault();
                        e.stopPropagation();
                        self._onCloseRequested();
                    });
            }

            self._$titleDiv = $titleDiv;
        },

        /* Creates the table.
        *************************************************************************/
        _createTable: function () {
            this._$table = $('<table></table>')
                .addClass('jtable')
                .appendTo(this._$mainContainer);

            if (this.options.tableId) {
                this._$table.attr('id', this.options.tableId);
            }

            this._jqueryuiThemeAddClass(this._$table, 'ui-widget-content');

            this._createTableHead();
            this._createTableBody();
        },

        /* Creates header (all column headers) of the table.
        *************************************************************************/
        _createTableHead: function () {
            var $thead = $('<thead></thead>')
                .appendTo(this._$table);

            this._addRowToTableHead($thead);
        },

        /* Adds tr element to given thead element
        *************************************************************************/
        _addRowToTableHead: function ($thead) {
            var $tr = $('<tr></tr>')
                .appendTo($thead);

            this._addColumnsToHeaderRow($tr);
        },

        /* Adds column header cells to given tr element.
        *************************************************************************/
        _addColumnsToHeaderRow: function ($tr) {
            for (var i = 0; i < this._columnList.length; i++) {
                var fieldName = this._columnList[i];
                var $headerCell = this._createHeaderCellForField(fieldName, this.options.fields[fieldName]);
                $headerCell.appendTo($tr);
            }
        },

        /* Creates a header cell for given field.
        *  Returns th jQuery object.
        *************************************************************************/
        _createHeaderCellForField: function (fieldName, field) {
            field.width = field.width || '10%'; //default column width: 10%.

            var $headerTextSpan = $('<span />')
                .addClass('jtable-column-header-text')
                .html(field.title);

            var $headerContainerDiv = $('<div />')
                .addClass('jtable-column-header-container')
                .append($headerTextSpan);

            var $th = $('<th></th>')
                .addClass('jtable-column-header')
                .addClass(field.listClass)
                .css('width', field.width)
                .data('fieldName', fieldName)
                .append($headerContainerDiv);

            this._jqueryuiThemeAddClass($th, 'ui-state-default');

            return $th;
        },

        /* Creates an empty header cell that can be used as command column headers.
        *************************************************************************/
        _createEmptyCommandHeader: function () {
            var $th = $('<th></th>')
                .addClass('jtable-command-column-header')
                .css('width', '1%');

            this._jqueryuiThemeAddClass($th, 'ui-state-default');

            return $th;
        },

        /* Creates tbody tag and adds to the table.
        *************************************************************************/
        _createTableBody: function () {
            this._$tableBody = $('<tbody></tbody>').appendTo(this._$table);
        },

        /* Creates a div to block UI while jTable is busy.
        *************************************************************************/
        _createBusyPanel: function () {
            this._$busyMessageDiv = $('<div />').addClass('jtable-busy-message').prependTo(this._$mainContainer);
            this._$busyDiv = $('<div />').addClass('jtable-busy-panel-background').prependTo(this._$mainContainer);
            this._jqueryuiThemeAddClass(this._$busyMessageDiv, 'ui-widget-header');
            this._hideBusy();
        },

        /* Creates and prepares error dialog div.
        *************************************************************************/
        _createErrorDialogDiv: function () {
            var self = this;

            self._$errorDialogDiv = $('<div></div>').appendTo(self._$mainContainer);
            self._$errorDialogDiv.dialog({
                autoOpen: false,
                show: self.options.dialogShowEffect,
                hide: self.options.dialogHideEffect,
                modal: true,
                dialogClass: "edit_order_dialog",
                title: self.options.messages.error,
                buttons: [{
                    text: self.options.messages.close,
                    click: function () {
                        self._$errorDialogDiv.dialog('close');
                    }
                }],
                open: function( event, ui ) {
                    var height = $( window ).height(),
                    dialog     = $( this ).closest( "div.ui-dialog" ).appendTo( "#navBars" );

                    dialog.offset({ top: (height - dialog.height()) / 2 - 10 });
                }
            });
        },

        /************************************************************************
        * PUBLIC METHODS                                                        *
        *************************************************************************/

        /* Loads data using AJAX call, clears table and fills with new data.
        *************************************************************************/
        load: function (postData, completeCallback) {
            if ( "undefined" === typeof postData )
                postData = this._loadPostDataSettings();
            this._lastPostData = postData;
            /* Order Handler - Added to save Sorting Selection */
            this._savePostDataSettings();
            this._reloadTable(completeCallback);
        },

        // _delete_cookie: function ( name ) {
        //     document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;';
        // },

        /* Order Handler - Saves user preferences for sorting
        *************************************************************************/
        _savePostDataSettings: function() {
            // if( null !== this._getCookie('postdata-sorting') ) {
            //     var key = this._cookieKeyPrefix + 'postdata-sorting';
            //     document.cookie = encodeURIComponent( key ) + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;';
            // }

            var params = "";

            if (!this.options.saveUserPreferences || "undefined" == typeof this._lastPostData) {
                return;
            }

            for(var name in this._lastPostData) {
                params = '{"status":"' + '&' + name + '=' + this._lastPostData[name] +  '"}';
            }

            if( "undefined" !== typeof params ) {
                this._setCookie('postdata-sorting', params);
            }
        },

        /* Order Handler - Loads user preferences for paging.
        *************************************************************************/
        _loadPostDataSettings: function() {
            if (!this.options.saveUserPreferences) {
                return;
            }

            var params = $.parseJSON(this._getCookie('postdata-sorting')),
            status     = "",
            param      = "",
            obj        = {};

            if ( null === params )
                return;
                            
            for ( var name in params ) {
               // status = name;
                param = params[ name ];
            }

            value = param.match(/=(.+)/);
            if ( null === value )
                return;
            value = value[1];

            status = param.match( /^&(.+)=/ );
            status = status[1];
            
            $( "#" + status ).val( value );

            if ( 'status_pf' == status && '1' == value ) {
                polling = true;
            }

            obj[ status ] = value;

            if ( params ) {
                return obj;
            }
        },

        /* Refreshes (re-loads) table data with last postData.
        *************************************************************************/
        reload: function (completeCallback) {
            this._reloadTable(completeCallback);
        },

        /* Gets a jQuery row object according to given record key
        *************************************************************************/
        getRowByKey: function (key) {
            for (var i = 0; i < this._$tableRows.length; i++) {
                if (key == this._getKeyValueOfRecord(this._$tableRows[i].data('record'))) {
                    return this._$tableRows[i];
                }
            }

            return null;
        },

        /* Completely removes the table from it's container.
        *************************************************************************/
        destroy: function () {
            this.element.empty();
            $.Widget.prototype.destroy.call(this);
        },

        /* Block the table while performing actions on it.
        *************************************************************************/
        showBusy: function (message,delay) {
            this._showBusy(message,delay);
        },
        
        /* UnBlock the table
        *************************************************************************/
        hideBusy: function (message) {
            this._hideBusy();
        },

        /************************************************************************
        * PRIVATE METHODS                                                       *
        *************************************************************************/

        /* Used to change options dynamically after initialization.
        *************************************************************************/
        _setOption: function (key, value) {

        },

        /* LOADING RECORDS  *****************************************************/

        /* Performs an AJAX call to reload data of the table.
        *************************************************************************/
        _reloadTable: function (completeCallback) {
            var self = this;

            var completeReload = function(data) {
                self._hideBusy();

                //Show the error message if server returns error
                if (data.Result != 'OK') {
                    self._showError(data.Message);
                    return;
                }

                //Re-generate table rows
                self._removeAllRows('reloading');
                self._addRecordsToTable(data.Records);

                self._onRecordsLoaded(data);

                //Call complete callback
                if (completeCallback) {
                    completeCallback();
                }
            };

            self._showBusy(self.options.messages.loadingMessage, self.options.loadingAnimationDelay); //Disable table since it's busy
            self._onLoadingRecords();

            //listAction may be a function, check if it is
            if ($.isFunction(self.options.actions.listAction)) {

                //Execute the function
                var funcResult = self.options.actions.listAction(self._lastPostData, self._createJtParamsForLoading());

                //Check if result is a jQuery Deferred object
                if (self._isDeferredObject(funcResult)) {
                    funcResult.done(function(data) {
                        completeReload(data);
                    }).fail(function() {
                        self._showError(self.options.messages.serverCommunicationError);
                    }).always(function() {
                        self._hideBusy();
                    });
                } else { //assume it's the data we're loading
                    completeReload(funcResult);
                }

            } else { //assume listAction as URL string.

                //Generate URL (with query string parameters) to load records
                var loadUrl = self._createRecordLoadUrl();

                //Load data from server using AJAX
                self._ajax({
                    url: loadUrl,
                    data: self._lastPostData,
                    success: function (data) {
                        completeReload(data);
                    },
                    error: function () {
                        self._hideBusy();
                        self._showError(self.options.messages.serverCommunicationError);
                    }
                });

            }
        },

        /* Creates URL to load records.
        *************************************************************************/
        _createRecordLoadUrl: function () {
            return this.options.actions.listAction;
        },

        _createJtParamsForLoading: function() {
            return {
                //Empty as default, paging, sorting or other extensions can override this method to add additional params to load request
            };
        },

        /* TABLE MANIPULATION METHODS *******************************************/

        /* Creates a row from given record
        *************************************************************************/
        _createRowFromRecord: function (record) {
            var $tr = $('<tr></tr>')
                .addClass('jtable-data-row')
                .attr('data-record-key', this._getKeyValueOfRecord(record))
                .data('record', record);

            this._addCellsToRowUsingRecord($tr);
            // $tr.find( '.tooltip_set').qtip({
            //             style: {
            //                 classes: 'qtip-bootstrap',
            //             },
            //             position: {
            //                 viewport: $(window)
            //             },
            //             show: {
            //                 // solo: true,
            //                 effect: function() {
            //                     $(this).fadeIn(400);
            //                 }
            //             },
            //             hide: {
            //                 distance: 30,
            //                 effect: function() {
            //                     $(this).fadeOut(400);
            //                 }
            //             }
            //         });
            if ( "true" == localStorage.toolTips )
                $( $tr ).find( '.tooltip_set').tlp();
            else
                $( $tr ).find( '.comments').tlp();

            return $tr;
        },

        /* Adds all cells to given row.
        *************************************************************************/
        _addCellsToRowUsingRecord: function ($row) {
            var record = $row.data('record');
            for (var i = 0; i < this._columnList.length; i++) {
                this._createCellForRecordField(record, this._columnList[i])
                    .appendTo($row);
            }
        },

        /* Create a cell for given field.
        *************************************************************************/
        _createCellForRecordField: function (record, fieldName) {
            return $('<td></td>')
                .addClass(this.options.fields[fieldName].listClass)
                .append((this._getDisplayTextForRecordField(record, fieldName)));
        },

        /* Adds a list of records to the table.
        *************************************************************************/
        _addRecordsToTable: function (records) {
            var self = this;

            $.each(records, function (index, record) {
                self._addRow(self._createRowFromRecord(record));
            });

            self._refreshRowStyles();
        },

        /* Adds a single row to the table.
        * NOTE: THIS METHOD IS DEPRECATED AND WILL BE REMOVED FROM FEATURE RELEASES.
        * USE _addRow METHOD.
        *************************************************************************/
        _addRowToTable: function ($tableRow, index, isNewRow, animationsEnabled) {
            var options = {
                index: this._normalizeNumber(index, 0, this._$tableRows.length, this._$tableRows.length)
            };

            if (isNewRow == true) {
                options.isNewRow = true;
            }

            if (animationsEnabled === false) {
                options.animationsEnabled = false;
            }

            this._addRow($tableRow, options);
        },

        /* Adds a single row to the table.
        *************************************************************************/
        _addRow: function ($row, options) {
            //Set defaults
            options = $.extend({
                index: this._$tableRows.length,
                isNewRow: false,
                animationsEnabled: true
            }, options);

            //Remove 'no data' row if this is first row
            if (this._$tableRows.length <= 0) {
                this._removeNoDataRow();
            }

            //Add new row to the table according to it's index
            options.index = this._normalizeNumber(options.index, 0, this._$tableRows.length, this._$tableRows.length);
            if (options.index == this._$tableRows.length) {
                //add as last row
                this._$tableBody.append($row);
                this._$tableRows.push($row);
            } else if (options.index === 0) {
                //add as first row
                this._$tableBody.prepend($row);
                this._$tableRows.unshift($row);
            } else {
                //insert to specified index
                this._$tableRows[options.index - 1].after($row);
                this._$tableRows.splice(options.index, 0, $row);
            }

            this._onRowInserted($row, options.isNewRow);

            //Show animation if needed
            if (options.isNewRow) {
                this._refreshRowStyles();
                if (this.options.animationsEnabled && options.animationsEnabled) {
                    this._showNewRowAnimation($row);
                }
            }
        },

        /* Shows created animation for a table row
        * TODO: Make this animation cofigurable and changable
        *************************************************************************/
        _showNewRowAnimation: function ($tableRow) {
            var className = 'jtable-row-created';
            if (this.options.jqueryuiTheme) {
                className = className + ' ui-state-highlight';
            }

            $tableRow.addClass(className, 'slow', '', function () {
                $tableRow.removeClass(className, 5000);
            });
        },

        /* Removes a row or rows (jQuery selection) from table.
        *************************************************************************/
        _removeRowsFromTable: function ($rows, reason) {
            var self = this;

            //Check if any row specified
            if ($rows.length <= 0) {
                return;
            }

            //remove from DOM
            $rows.addClass('jtable-row-removed').remove();

            //remove from _$tableRows array
            $rows.each(function () {
                var index = self._findRowIndex($(this));
                if (index >= 0) {
                    self._$tableRows.splice(index, 1);
                }
            });

            self._onRowsRemoved($rows, reason);

            //Add 'no data' row if all rows removed from table
            if (self._$tableRows.length == 0) {
                self._addNoDataRow();
            }

            self._refreshRowStyles();
        },

        /* Finds index of a row in table.
        *************************************************************************/
        _findRowIndex: function ($row) {
            return this._findIndexInArray($row, this._$tableRows, function ($row1, $row2) {
                return $row1.data('record') == $row2.data('record');
            });
        },

        /* Removes all rows in the table and adds 'no data' row.
        *************************************************************************/
        _removeAllRows: function (reason) {
            //If no rows does exists, do nothing
            if (this._$tableRows.length <= 0) {
                return;
            }

            //Select all rows (to pass it on raising _onRowsRemoved event)
            var $rows = this._$tableBody.find('tr.jtable-data-row');

            //Remove all rows from DOM and the _$tableRows array
            this._$tableBody.empty();
            this._$tableRows = [];

            this._onRowsRemoved($rows, reason);

            //Add 'no data' row since we removed all rows
            this._addNoDataRow();
        },

        /* Adds "no data available" row to the table.
        *************************************************************************/
        _addNoDataRow: function () {
            if (this._$tableBody.find('>tr.jtable-no-data-row').length > 0) {
                return;
            }

            var $tr = $('<tr></tr>')
                .addClass('jtable-no-data-row')
                .appendTo(this._$tableBody);

            var totalColumnCount = this._$table.find('thead th').length;
            $('<td></td>')
                .attr('colspan', totalColumnCount)
                .html(this.options.messages.noDataAvailable)
                .appendTo($tr);
        },

        /* Removes "no data available" row from the table.
        *************************************************************************/
        _removeNoDataRow: function () {
            this._$tableBody.find('.jtable-no-data-row').remove();
        },

        /* Refreshes styles of all rows in the table
        *************************************************************************/
        _refreshRowStyles: function () {
            for (var i = 0; i < this._$tableRows.length; i++) {
                if (i % 2 == 0) {
                    this._$tableRows[i].addClass('jtable-row-even');
                } else {
                    this._$tableRows[i].removeClass('jtable-row-even');
                }
            }
        },

        /* RENDERING FIELD VALUES ***********************************************/

        /* Gets text for a field of a record according to it's type.
        *************************************************************************/
        _getDisplayTextForRecordField: function (record, fieldName) {
            var field = this.options.fields[fieldName];
            var fieldValue = record[fieldName];

            //if this is a custom field, call display function
            if (field.display) {
                return field.display({ record: record });
            }

            if (field.type == 'date') {
                return this._getDisplayTextForDateRecordField(field, fieldValue);
            } else if (field.type == 'checkbox') {
                return this._getCheckBoxTextForFieldByValue(fieldName, fieldValue);
            } else if (field.options) { //combobox or radio button list since there are options.
                var options = this._getOptionsForField(fieldName, {
                    record: record,
                    value: fieldValue,
                    source: 'list',
                    dependedValues: this._createDependedValuesUsingRecord(record, field.dependsOn)
                });
                return this._findOptionByValue(options, fieldValue).DisplayText;
            } else { //other types
                return fieldValue;
            }
        },

        /* Creates and returns an object that's properties are depended values of a record.
        *************************************************************************/
        _createDependedValuesUsingRecord: function (record, dependsOn) {
            if (!dependsOn) {
                return {};
            }

            var dependedValues = {};
            for (var i = 0; i < dependsOn.length; i++) {
                dependedValues[dependsOn[i]] = record[dependsOn[i]];
            }

            return dependedValues;
        },

        /* Finds an option object by given value.
        *************************************************************************/
        _findOptionByValue: function (options, value) {
            for (var i = 0; i < options.length; i++) {
                if (options[i].Value == value) {
                    return options[i];
                }
            }

            return {}; //no option found
        },

        /* Gets text for a date field.
        *************************************************************************/
        _getDisplayTextForDateRecordField: function (field, fieldValue) {
            if (!fieldValue) {
                return '';
            }

            var displayFormat = field.displayFormat || this.options.defaultDateFormat;
            var date = this._parseDate(fieldValue);
            return $.datepicker.formatDate(displayFormat, date);
        },

        /* Gets options for a field according to user preferences.
        *************************************************************************/
        _getOptionsForField: function (fieldName, funcParams) {
            var field = this.options.fields[fieldName];
            var optionsSource = field.options;

            if ($.isFunction(optionsSource)) {
                //prepare parameter to the function
                funcParams = $.extend(true, {
                    _cacheCleared: false,
                    dependedValues: {},
                    clearCache: function () {
                        this._cacheCleared = true;
                    }
                }, funcParams);

                //call function and get actual options source
                optionsSource = optionsSource(funcParams);
            }

            var options;

            //Build options according to it's source type
            if (typeof optionsSource == 'string') { //It is an Url to download options
                var cacheKey = 'options_' + fieldName + '_' + optionsSource; //create a unique cache key
                if (funcParams._cacheCleared || (!this._cache[cacheKey])) {
                    //if user calls clearCache() or options are not found in the cache, download options
                    this._cache[cacheKey] = this._buildOptionsFromArray(this._downloadOptions(fieldName, optionsSource));
                    this._sortFieldOptions(this._cache[cacheKey], field.optionsSorting);
                } else {
                    //found on cache..
                    //if this method (_getOptionsForField) is called to get option for a specific value (on funcParams.source == 'list')
                    //and this value is not in cached options, we need to re-download options to get the unfound (probably new) option.
                    if (funcParams.value != undefined) {
                        var optionForValue = this._findOptionByValue(this._cache[cacheKey], funcParams.value);
                        if (optionForValue.DisplayText == undefined) { //this value is not in cached options...
                            this._cache[cacheKey] = this._buildOptionsFromArray(this._downloadOptions(fieldName, optionsSource));
                            this._sortFieldOptions(this._cache[cacheKey], field.optionsSorting);
                        }
                    }
                }

                options = this._cache[cacheKey];
            } else if (jQuery.isArray(optionsSource)) { //It is an array of options
                options = this._buildOptionsFromArray(optionsSource);
                this._sortFieldOptions(options, field.optionsSorting);
            } else { //It is an object that it's properties are options
                options = this._buildOptionsArrayFromObject(optionsSource);
                this._sortFieldOptions(options, field.optionsSorting);
            }

            return options;
        },

        /* Download options for a field from server.
        *************************************************************************/
        _downloadOptions: function (fieldName, url) {
            var self = this;
            var options = [];

            self._ajax({
                url: url,
                async: false,
                success: function (data) {
                    if (data.Result != 'OK') {
                        self._showError(data.Message);
                        return;
                    }

                    options = data.Options;
                },
                error: function () {
                    var errMessage = self._formatString(self.options.messages.cannotLoadOptionsFor, fieldName);
                    self._showError(errMessage);
                }
            });

            return options;
        },

        /* Sorts given options according to sorting parameter.
        *  sorting can be: 'value', 'value-desc', 'text' or 'text-desc'.
        *************************************************************************/
        _sortFieldOptions: function (options, sorting) {

            if ((!options) || (!options.length) || (!sorting)) {
                return;
            }

            //Determine using value of text
            var dataSelector;
            if (sorting.indexOf('value') == 0) {
                dataSelector = function (option) {
                    return option.Value;
                };
            } else { //assume as text
                dataSelector = function (option) {
                    return option.DisplayText;
                };
            }

            var compareFunc;
            if ($.type(dataSelector(options[0])) == 'string') {
                compareFunc = function (option1, option2) {
                    return dataSelector(option1).localeCompare(dataSelector(option2));
                };
            } else { //asuume as numeric
                compareFunc = function (option1, option2) {
                    return dataSelector(option1) - dataSelector(option2);
                };
            }

            if (sorting.indexOf('desc') > 0) {
                options.sort(function (a, b) {
                    return compareFunc(b, a);
                });
            } else { //assume as asc
                options.sort(function (a, b) {
                    return compareFunc(a, b);
                });
            }
        },

        /* Creates an array of options from given object.
        *************************************************************************/
        _buildOptionsArrayFromObject: function (options) {
            var list = [];

            $.each(options, function (propName, propValue) {
                list.push({
                    Value: propName,
                    DisplayText: propValue
                });
            });

            return list;
        },

        /* Creates array of options from giving options array.
        *************************************************************************/
        _buildOptionsFromArray: function (optionsArray) {
            var list = [];

            for (var i = 0; i < optionsArray.length; i++) {
                if ($.isPlainObject(optionsArray[i])) {
                    list.push(optionsArray[i]);
                } else { //assumed as primitive type (int, string...)
                    list.push({
                        Value: optionsArray[i],
                        DisplayText: optionsArray[i]
                    });
                }
            }

            return list;
        },

        /* Parses given date string to a javascript Date object.
        *  Given string must be formatted one of the samples shown below:
        *  /Date(1320259705710)/
        *  2011-01-01 20:32:42 (YYYY-MM-DD HH:MM:SS)
        *  2011-01-01 (YYYY-MM-DD)
        *************************************************************************/
        _parseDate: function (dateString) {
            if (dateString.indexOf('Date') >= 0) { //Format: /Date(1320259705710)/
                return new Date(
                    parseInt(dateString.substr(6), 10)
                );
            } else if (dateString.length == 10) { //Format: 2011-01-01
                return new Date(
                    parseInt(dateString.substr(0, 4), 10),
                    parseInt(dateString.substr(5, 2), 10) - 1,
                    parseInt(dateString.substr(8, 2), 10)
                );
            } else if (dateString.length == 19) { //Format: 2011-01-01 20:32:42
                return new Date(
                    parseInt(dateString.substr(0, 4), 10),
                    parseInt(dateString.substr(5, 2), 10) - 1,
                    parseInt(dateString.substr(8, 2, 10)),
                    parseInt(dateString.substr(11, 2), 10),
                    parseInt(dateString.substr(14, 2), 10),
                    parseInt(dateString.substr(17, 2), 10)
                );
            } else {
                this._logWarn('Given date is not properly formatted: ' + dateString);
                return 'format error!';
            }
        },

        /* TOOL BAR *************************************************************/

        /* Creates the toolbar.
        *************************************************************************/
        _createToolBar: function () {
            this._$toolbarDiv = $('<div />')
            .addClass('jtable-toolbar')
            .appendTo(this._$titleDiv);

            for (var i = 0; i < this.options.toolbar.items.length; i++) {
                this._addToolBarItem(this.options.toolbar.items[i]);
            }
        },

        /* Adds a new item to the toolbar.
        *************************************************************************/
        _addToolBarItem: function (item) {

            //Check if item is valid
            if ((item == undefined) || (item.text == undefined && item.icon == undefined)) {
                this._logWarn('Can not add tool bar item since it is not valid!');
                this._logWarn(item);
                return null;
            }

            var $toolBarItem = $('<span></span>')
                .addClass('jtable-toolbar-item')
                .appendTo(this._$toolbarDiv);

            this._jqueryuiThemeAddClass($toolBarItem, 'ui-widget ui-state-default ui-corner-all', 'ui-state-hover');

            //cssClass property
            if (item.cssClass) {
                $toolBarItem
                    .addClass(item.cssClass);
            }

            //tooltip property
            if (item.tooltip) {
                $toolBarItem
                    .attr('title', item.tooltip);
            }

            //icon property
            if (item.icon) {
                var $icon = $('<span class="jtable-toolbar-item-icon"></span>').appendTo($toolBarItem);
                if (item.icon === true) {
                    //do nothing
                } else if ($.type(item.icon === 'string')) {
                    $icon.css('background', 'url("' + item.icon + '")');
                }
            }

            //Added for Order Handler - css icon property
            if (item.iconCSS) {
                var $iconCSS = $('<span class="jtable-toolbar-item-icon"></span>').appendTo($toolBarItem);
                if (item.iconCSS === true) {
                    //do nothing
                } else if ($.type(item.iconCSS === 'string')) {
                    var $iconCSSimage = $('<i class="fa ' + item.iconCSS + '"></i>').appendTo($iconCSS);
                }
            }

            //text property
            if (item.text) {
                $('<span class=""></span>')
                    .html(item.text)
                    .addClass('jtable-toolbar-item-text').appendTo($toolBarItem);
            }

            //click event
            if (item.click) {
                $toolBarItem.click(function () {
                    item.click();
                });
            }

            //set hover animation parameters
            var hoverAnimationDuration = undefined;
            var hoverAnimationEasing = undefined;
            if (this.options.toolbar.hoverAnimation) {
                hoverAnimationDuration = this.options.toolbar.hoverAnimationDuration;
                hoverAnimationEasing = this.options.toolbar.hoverAnimationEasing;
            }

            //change class on hover
            $toolBarItem.hover(function () {
                $toolBarItem.addClass('jtable-toolbar-item-hover', hoverAnimationDuration, hoverAnimationEasing);
            }, function () {
                $toolBarItem.removeClass('jtable-toolbar-item-hover', hoverAnimationDuration, hoverAnimationEasing);
            });

            return $toolBarItem;
        },

        /* ERROR DIALOG *********************************************************/

        /* Shows error message dialog with given message.
        *************************************************************************/
        _showError: function (message) {
            this._$errorDialogDiv.html(message).dialog('open');
        },

        /* BUSY PANEL ***********************************************************/

        /* Shows busy indicator and blocks table UI.
        * TODO: Make this cofigurable and changable
        *************************************************************************/
        _setBusyTimer: null,
        _showBusy: function (message, delay) {
            var self = this;  //

            //Show a transparent overlay to prevent clicking to the table
            self._$busyDiv
                .width(self._$mainContainer.width())
                .height(self._$mainContainer.height())
                .addClass('jtable-busy-panel-background-invisible')
                .show();

            var makeVisible = function () {
                self._$busyDiv.removeClass('jtable-busy-panel-background-invisible');
                self._$busyMessageDiv.html(message).show();
            };

            if (delay) {
                if (self._setBusyTimer) {
                    return;
                }

                self._setBusyTimer = setTimeout(makeVisible, delay);
            } else {
                makeVisible();
            }
        },

        /* Hides busy indicator and unblocks table UI.
        *************************************************************************/
        _hideBusy: function () {
            clearTimeout(this._setBusyTimer);
            this._setBusyTimer = null;
            this._$busyDiv.hide();
            this._$busyMessageDiv.html('').hide();
        },

        /* Returns true if jTable is busy.
        *************************************************************************/
        _isBusy: function () {
            return this._$busyMessageDiv.is(':visible');
        },

        /* Adds jQueryUI class to an item.
        *************************************************************************/
        _jqueryuiThemeAddClass: function ($elm, className, hoverClassName) {
            if (!this.options.jqueryuiTheme) {
                return;
            }

            $elm.addClass(className);

            if (hoverClassName) {
                $elm.hover(function () {
                    $elm.addClass(hoverClassName);
                }, function () {
                    $elm.removeClass(hoverClassName);
                });
            }
        },

        /* COMMON METHODS *******************************************************/

        /* Performs an AJAX call to specified URL.
        * THIS METHOD IS DEPRECATED AND WILL BE REMOVED FROM FEATURE RELEASES.
        * USE _ajax METHOD.
        *************************************************************************/
        _performAjaxCall: function (url, postData, async, success, error) {
            this._ajax({
                url: url,
                data: postData,
                async: async,
                success: success,
                error: error
            });
        },

        _unAuthorizedRequestHandler: function() {
            if (this.options.unAuthorizedRequestRedirectUrl) {
                location.href = this.options.unAuthorizedRequestRedirectUrl;
            } else {
                location.reload(true);
            }
        },

        /* This method is used to perform AJAX calls in jTable instead of direct
        * usage of jQuery.ajax method.
        *************************************************************************/
        _ajax: function (options) {
            var self = this;

            //Handlers for HTTP status codes
            var opts = {
                statusCode: {
                    401: function () { //Unauthorized
                        self._unAuthorizedRequestHandler();
                    }
                },
                global: false,
            };

            opts = $.extend(opts, this.options.ajaxSettings, options);

            //Override success
            opts.success = function (data) {
                //Checking for Authorization error
                if (data && data.UnAuthorizedRequest == true) {
                    self._unAuthorizedRequestHandler();
                }

                if (options.success) {
                    options.success(data);
                }
            };

            //Override error
            opts.error = function (jqXHR, textStatus, errorThrown) {
                if (unloadingPage) {
                    jqXHR.abort();
                    return;
                }
                
                if (options.error) {
                    options.error(arguments);
                }
            };

            //Override complete
            opts.complete = function () {
                if (options.complete) {
                    options.complete();
                }
            };

            $.ajax(opts);
        },

        /* Gets value of key field of a record.
        *************************************************************************/
        _getKeyValueOfRecord: function (record) {
            return record[this._keyField];
        },

        /************************************************************************
        * COOKIE                                                                *
        *************************************************************************/

        /* Sets a cookie with given key.
        *************************************************************************/
        _setCookie: function (key, value) {
            key = this._cookieKeyPrefix + key;

            var expireDate = new Date();
            expireDate.setDate(expireDate.getDate() + 30);
            document.cookie = encodeURIComponent(key) + '=' + encodeURIComponent(value) + "; expires=" + expireDate.toUTCString();
        },

        /* Gets a cookie with given key.
        *************************************************************************/
        _getCookie: function (key) {
            key = this._cookieKeyPrefix + key;

            var equalities = document.cookie.split('; ');
            for (var i = 0; i < equalities.length; i++) {
                if (!equalities[i]) {
                    continue;
                }

                var splitted = equalities[i].split('=');
                if (splitted.length != 2) {
                    continue;
                }

                if (decodeURIComponent(splitted[0]) === key) {
                    return decodeURIComponent(splitted[1] || '');
                }
            }

            return null;
        },

        /* Generates a hash key to be prefix for all cookies for this jtable instance.
        *************************************************************************/
        _generateCookieKeyPrefix: function () {

            var simpleHash = function (value) {
                var hash = 0;
                if (value.length == 0) {
                    return hash;
                }

                for (var i = 0; i < value.length; i++) {
                    var ch = value.charCodeAt(i);
                    hash = ((hash << 5) - hash) + ch;
                    hash = hash & hash;
                }

                return hash;
            };

            var strToHash = '';
            if (this.options.tableId) {
                strToHash = strToHash + this.options.tableId + '#';
            }

            strToHash = strToHash + this._columnList.join('$') + '#c' + this._$table.find('thead th').length;
            return 'jtable#' + simpleHash(strToHash);
        },

        /************************************************************************
        * EVENT RAISING METHODS                                                 *
        *************************************************************************/

        _onLoadingRecords: function () {
            this._trigger("loadingRecords", null, {});
        },

        _onRecordsLoaded: function (data) {
            this._trigger("recordsLoaded", null, { records: data.Records, serverResponse: data });
        },

        _onRowInserted: function ($row, isNewRow) {
            this._trigger("rowInserted", null, { row: $row, record: $row.data('record'), isNewRow: isNewRow });
        },

        _onRowsRemoved: function ($rows, reason) {
            this._trigger("rowsRemoved", null, { rows: $rows, reason: reason });
        },

        _onCloseRequested: function () {
            this._trigger("closeRequested", null, {});
        }

    });

}(jQuery));


/************************************************************************
* Some UTULITY methods used by jTable                                   *
*************************************************************************/
(function ($) {

    $.extend(true, $.hik.jtable.prototype, {

        /* Gets property value of an object recursively.
        *************************************************************************/
        _getPropertyOfObject: function (obj, propName) {
            if (propName.indexOf('.') < 0) {
                return obj[propName];
            } else {
                var preDot = propName.substring(0, propName.indexOf('.'));
                var postDot = propName.substring(propName.indexOf('.') + 1);
                return this._getPropertyOfObject(obj[preDot], postDot);
            }
        },

        /* Sets property value of an object recursively.
        *************************************************************************/
        _setPropertyOfObject: function (obj, propName, value) {
            if (propName.indexOf('.') < 0) {
                obj[propName] = value;
            } else {
                var preDot = propName.substring(0, propName.indexOf('.'));
                var postDot = propName.substring(propName.indexOf('.') + 1);
                this._setPropertyOfObject(obj[preDot], postDot, value);
            }
        },

        /* Inserts a value to an array if it does not exists in the array.
        *************************************************************************/
        _insertToArrayIfDoesNotExists: function (array, value) {
            if ($.inArray(value, array) < 0) {
                array.push(value);
            }
        },

        /* Finds index of an element in an array according to given comparision function
        *************************************************************************/
        _findIndexInArray: function (value, array, compareFunc) {

            //If not defined, use default comparision
            if (!compareFunc) {
                compareFunc = function (a, b) {
                    return a == b;
                };
            }

            for (var i = 0; i < array.length; i++) {
                if (compareFunc(value, array[i])) {
                    return i;
                }
            }

            return -1;
        },

        /* Normalizes a number between given bounds or sets to a defaultValue
        *  if it is undefined
        *************************************************************************/
        _normalizeNumber: function (number, min, max, defaultValue) {
            if (number == undefined || number == null || isNaN(number)) {
                return defaultValue;
            }

            if (number < min) {
                return min;
            }

            if (number > max) {
                return max;
            }

            return number;
        },

        /* Formats a string just like string.format in c#.
        *  Example:
        *  _formatString('Hello {0}','Halil') = 'Hello Halil'
        *************************************************************************/
        _formatString: function () {
            if (arguments.length === 0) {
                return null;
            }

            var str = arguments[0];
            for (var i = 1; i < arguments.length; i++) {
                var placeHolder = '{' + (i - 1) + '}';
                str = str.replace(placeHolder, arguments[i]);
            }

            return str;
        },

        /* Checks if given object is a jQuery Deferred object.
         */
        _isDeferredObject: function (obj) {
            return obj.then && obj.done && obj.fail;
        },

        //Logging methods ////////////////////////////////////////////////////////

        _logDebug: function (text) {
            if (!window.console) {
                return;
            }

            console.log('jTable DEBUG: ' + text);
        },

        _logInfo: function (text) {
            if (!window.console) {
                return;
            }

            console.log('jTable INFO: ' + text);
        },

        _logWarn: function (text) {
            if (!window.console) {
                return;
            }

            console.log('jTable WARNING: ' + text);
        },

        _logError: function (text) {
            if (!window.console) {
                return;
            }

            console.log('jTable ERROR: ' + text);
        }

    });

    /* Fix for array.indexOf method in IE7.
     * This code is taken from http://www.tutorialspoint.com/javascript/array_indexof.htm */
    if (!Array.prototype.indexOf) {
        Array.prototype.indexOf = function (elt) {
            var len = this.length;
            var from = Number(arguments[1]) || 0;
            from = (from < 0)
                 ? Math.ceil(from)
                 : Math.floor(from);
            if (from < 0)
                from += len;
            for (; from < len; from++) {
                if (from in this &&
                    this[from] === elt)
                    return from;
            }
            return -1;
        };
    }

})(jQuery);


/************************************************************************
* FORMS extension for jTable (base for edit/create forms)               *
*************************************************************************/
(function ($) {

    $.extend(true, $.hik.jtable.prototype, {

        /************************************************************************
        * PRIVATE METHODS                                                       *
        *************************************************************************/

        /* Submits a form asynchronously using AJAX.
        *  This method is needed, since form submitting logic can be overrided
        *  by extensions.
        *************************************************************************/
        _submitFormUsingAjax: function (url, formData, success, error) {
            this._ajax({
                url: url,
                data: formData,
                success: success,
                error: error
            });
        },

        /* Creates label for an input element.
        *************************************************************************/
        _createInputLabelForRecordField: function (fieldName) {
            //TODO: May create label tag instead of a div.
            return $('<div />')
                .addClass('jtable-input-label')
                .html(this.options.fields[fieldName].inputTitle || this.options.fields[fieldName].title);
        },

        /* Creates an input element according to field type.
        *************************************************************************/
        _createInputForRecordField: function (funcParams) {
            var fieldName = funcParams.fieldName,
                value = funcParams.value,
                record = funcParams.record,
                formType = funcParams.formType,
                form = funcParams.form;

            //Get the field
            var field = this.options.fields[fieldName];

            //If value if not supplied, use defaultValue of the field
            if (value == undefined || value == null) {
                value = field.defaultValue;
            }

            //Use custom function if supplied
            if (field.input) {
                var $input = $(field.input({
                    value: value,
                    record: record,
                    formType: formType,
                    form: form
                }));

                //Add id attribute if does not exists
                if (!$input.attr('id')) {
                    $input.attr('id', 'Edit-' + fieldName);
                }

                //Wrap input element with div
                return $('<div />')
                    .addClass('jtable-input jtable-custom-input')
                    .append($input);
            }

            //Create input according to field type
            if (field.type == 'date') {
                return this._createDateInputForField(field, fieldName, value);
            } else if (field.type == 'textarea') {
                return this._createTextAreaForField(field, fieldName, value);
            } else if (field.type == 'password') {
                return this._createPasswordInputForField(field, fieldName, value);
            } else if (field.type == 'checkbox') {
                return this._createCheckboxForField(field, fieldName, value);
            } else if (field.options) {
                if (field.type == 'radiobutton') {
                    return this._createRadioButtonListForField(field, fieldName, value, record, formType);
                } else {
                    return this._createDropDownListForField(field, fieldName, value, record, formType, form);
                }
            } else {
                return this._createTextInputForField(field, fieldName, value);
            }
        },

        //Creates a hidden input element with given name and value.
        _createInputForHidden: function (fieldName, value) {
            if (value == undefined) {
                value = "";
            }

            return $('<input type="hidden" name="' + fieldName + '" id="Edit-' + fieldName + '"></input>')
                .val(value);
        },

        /* Creates a date input for a field.
        *************************************************************************/
        _createDateInputForField: function (field, fieldName, value) {
            var $input = $('<input class="' + field.inputClass + '" id="Edit-' + fieldName + '" type="text" name="' + fieldName + '"></input>');
            if(value !== undefined) {
                $input.val(value);
            }
            
            var displayFormat = field.displayFormat || this.options.defaultDateFormat;
            $input.datepicker({ dateFormat: displayFormat });
            return $('<div />')
                .addClass('jtable-input jtable-date-input')
                .append($input);
        },

        /* Creates a textarea element for a field.
        *************************************************************************/
        _createTextAreaForField: function (field, fieldName, value) {
            var $textArea = $('<textarea class="' + field.inputClass + '" id="Edit-' + fieldName + '" name="' + fieldName + '"></textarea>');
            if (value != undefined) {
                $textArea.val(value);
            }
            
            return $('<div />')
                .addClass('jtable-input jtable-textarea-input')
                .append($textArea);
        },

        /* Creates a standart textbox for a field.
        *************************************************************************/
        _createTextInputForField: function (field, fieldName, value) {
            var $input = $('<input class="' + field.inputClass + '" id="Edit-' + fieldName + '" type="text" name="' + fieldName + '"></input>');
            if (value != undefined) {
                $input.val(value);
            }
            
            return $('<div />')
                .addClass('jtable-input jtable-text-input')
                .append($input);
        },

        /* Creates a password input for a field.
        *************************************************************************/
        _createPasswordInputForField: function (field, fieldName, value) {
            var $input = $('<input class="' + field.inputClass + '" id="Edit-' + fieldName + '" type="password" name="' + fieldName + '"></input>');
            if (value != undefined) {
                $input.val(value);
            }
            
            return $('<div />')
                .addClass('jtable-input jtable-password-input')
                .append($input);
        },

        /* Creates a checkboxfor a field.
        *************************************************************************/
        _createCheckboxForField: function (field, fieldName, value) {
            var self = this;

            //If value is undefined, get unchecked state's value
            if (value == undefined) {
                value = self._getCheckBoxPropertiesForFieldByState(fieldName, false).Value;
            }

            //Create a container div
            var $containerDiv = $('<div />')
                .addClass('jtable-input jtable-checkbox-input');

            //Create checkbox and check if needed
            var $checkBox = $('<input class="' + field.inputClass + '" id="Edit-' + fieldName + '" type="checkbox" name="' + fieldName + '" />')
                .appendTo($containerDiv);
            if (value != undefined) {
                $checkBox.val(value);
            }

            //Create display text of checkbox for current state
            var $textSpan = $('<span>' + (field.formText || self._getCheckBoxTextForFieldByValue(fieldName, value)) + '</span>')
                .appendTo($containerDiv);

            //Check the checkbox if it's value is checked-value
            if (self._getIsCheckBoxSelectedForFieldByValue(fieldName, value)) {
                $checkBox.attr('checked', 'checked');
            }

            //This method sets checkbox's value and text according to state of the checkbox
            var refreshCheckBoxValueAndText = function () {
                var checkboxProps = self._getCheckBoxPropertiesForFieldByState(fieldName, $checkBox.is(':checked'));
                $checkBox.attr('value', checkboxProps.Value);
                $textSpan.html(field.formText || checkboxProps.DisplayText);
            };

            //Register to click event to change display text when state of checkbox is changed.
            $checkBox.click(function () {
                refreshCheckBoxValueAndText();
            });

            //Change checkbox state when clicked to text
            if (field.setOnTextClick != false) {
                $textSpan
                    .addClass('jtable-option-text-clickable')
                    .click(function () {
                        if ($checkBox.is(':checked')) {
                            $checkBox.attr('checked', false);
                        } else {
                            $checkBox.attr('checked', true);
                        }

                        refreshCheckBoxValueAndText();
                    });
            }

            return $containerDiv;
        },

        /* Creates a drop down list (combobox) input element for a field.
        *************************************************************************/
        _createDropDownListForField: function (field, fieldName, value, record, source, form) {

            //Create a container div
            var $containerDiv = $('<div />')
                .addClass('jtable-input jtable-dropdown-input');

            //Create select element
            var $select = $('<select class="' + field.inputClass + '" id="Edit-' + fieldName + '" name="' + fieldName + '"></select>')
                .appendTo($containerDiv);

            //add options
            var options = this._getOptionsForField(fieldName, {
                record: record,
                source: source,
                form: form,
                dependedValues: this._createDependedValuesUsingForm(form, field.dependsOn)
            });

            this._fillDropDownListWithOptions($select, options, value);

            return $containerDiv;
        },
        
        /* Fills a dropdown list with given options.
        *************************************************************************/
        _fillDropDownListWithOptions: function ($select, options, value) {
            $select.empty();
            for (var i = 0; i < options.length; i++) {
                $('<option' + (options[i].Value == value ? ' selected="selected"' : '') + '>' + options[i].DisplayText + '</option>')
                    .val(options[i].Value)
                    .appendTo($select);
            }
        },

        /* Creates depended values object from given form.
        *************************************************************************/
        _createDependedValuesUsingForm: function ($form, dependsOn) {
            if (!dependsOn) {
                return {};
            }

            var dependedValues = {};

            for (var i = 0; i < dependsOn.length; i++) {
                var dependedField = dependsOn[i];

                var $dependsOn = $form.find('select[name=' + dependedField + ']');
                if ($dependsOn.length <= 0) {
                    continue;
                }

                dependedValues[dependedField] = $dependsOn.val();
            }


            return dependedValues;
        },

        /* Creates a radio button list for a field.
        *************************************************************************/
        _createRadioButtonListForField: function (field, fieldName, value, record, source) {
            var $containerDiv = $('<div />')
                .addClass('jtable-input jtable-radiobuttonlist-input');

            var options = this._getOptionsForField(fieldName, {
                record: record,
                source: source
            });

            $.each(options, function(i, option) {
                var $radioButtonDiv = $('<div class=""></div>')
                    .addClass('jtable-radio-input')
                    .appendTo($containerDiv);

                var $radioButton = $('<input type="radio" id="Edit-' + fieldName + '-' + i + '" class="' + field.inputClass + '" name="' + fieldName + '"' + ((option.Value == (value + '')) ? ' checked="true"' : '') + ' />')
                    .val(option.Value)
                    .appendTo($radioButtonDiv);

                var $textSpan = $('<span></span>')
                    .html(option.DisplayText)
                    .appendTo($radioButtonDiv);

                if (field.setOnTextClick != false) {
                    $textSpan
                        .addClass('jtable-option-text-clickable')
                        .click(function () {
                            if (!$radioButton.is(':checked')) {
                                $radioButton.attr('checked', true);
                            }
                        });
                }
            });

            return $containerDiv;
        },

        /* Gets display text for a checkbox field.
        *************************************************************************/
        _getCheckBoxTextForFieldByValue: function (fieldName, value) {
            return this.options.fields[fieldName].values[value];
        },

        /* Returns true if given field's value must be checked state.
        *************************************************************************/
        _getIsCheckBoxSelectedForFieldByValue: function (fieldName, value) {
            return (this._createCheckBoxStateArrayForFieldWithCaching(fieldName)[1].Value.toString() == value.toString());
        },

        /* Gets an object for a checkbox field that has Value and DisplayText
        *  properties.
        *************************************************************************/
        _getCheckBoxPropertiesForFieldByState: function (fieldName, checked) {
            return this._createCheckBoxStateArrayForFieldWithCaching(fieldName)[(checked ? 1 : 0)];
        },

        /* Calls _createCheckBoxStateArrayForField with caching.
        *************************************************************************/
        _createCheckBoxStateArrayForFieldWithCaching: function (fieldName) {
            var cacheKey = 'checkbox_' + fieldName;
            if (!this._cache[cacheKey]) {

                this._cache[cacheKey] = this._createCheckBoxStateArrayForField(fieldName);
            }

            return this._cache[cacheKey];
        },

        /* Creates a two element array of objects for states of a checkbox field.
        *  First element for unchecked state, second for checked state.
        *  Each object has two properties: Value and DisplayText
        *************************************************************************/
        _createCheckBoxStateArrayForField: function (fieldName) {
            var stateArray = [];
            var currentIndex = 0;
            $.each(this.options.fields[fieldName].values, function (propName, propValue) {
                if (currentIndex++ < 2) {
                    stateArray.push({ 'Value': propName, 'DisplayText': propValue });
                }
            });

            return stateArray;
        },

        /* Searches a form for dependend dropdowns and makes them cascaded.
        */
        _makeCascadeDropDowns: function ($form, record, source) {
            var self = this;

            $form.find('select') //for each combobox
                .each(function () {
                    var $thisDropdown = $(this);

                    //get field name
                    var fieldName = $thisDropdown.attr('name');
                    if (!fieldName) {
                        return;
                    }

                    var field = self.options.fields[fieldName];
                    
                    //check if this combobox depends on others
                    if (!field.dependsOn) {
                        return;
                    }

                    //for each dependency
                    $.each(field.dependsOn, function (index, dependsOnField) {
                        //find the depended combobox
                        var $dependsOnDropdown = $form.find('select[name=' + dependsOnField + ']');
                        //when depended combobox changes
                        $dependsOnDropdown.change(function () {

______________________________________________________

 

* End Part 2 *

______________________________________________________

Link to comment
Share on other sites

Replace ./admin/includes/modules/order_handler/js/01_02_jquery.jtable.js With This: (2/3 Part of this File)


                            //Refresh options
                            var funcParams = {
                                record: record,
                                source: source,
                                form: $form,
                                dependedValues: {}
                            };
                            funcParams.dependedValues = self._createDependedValuesUsingForm($form, field.dependsOn);
                            var options = self._getOptionsForField(fieldName, funcParams);

                            //Fill combobox with new options
                            self._fillDropDownListWithOptions($thisDropdown, options, undefined);

                            //Thigger change event to refresh multi cascade dropdowns.
                            $thisDropdown.change();
                        });
                    });
                });
        },

        /* Updates values of a record from given form
        *************************************************************************/
        _updateRecordValuesFromForm: function (record, $form) {
            for (var i = 0; i < this._fieldList.length; i++) {
                var fieldName = this._fieldList[i];
                var field = this.options.fields[fieldName];

                //Do not update non-editable fields
                if (field.edit == false) {
                    continue;
                }

                //Get field name and the input element of this field in the form
                var $inputElement = $form.find('[name="' + fieldName + '"]');
                if ($inputElement.length <= 0) {
                    continue;
                }

                //Update field in record according to it's type
                if (field.type == 'date') {
                    var dateVal = $inputElement.val();
                    if (dateVal) {
                        var displayFormat = field.displayFormat || this.options.defaultDateFormat;
                        try {
                            var date = $.datepicker.parseDate(displayFormat, dateVal);
                            record[fieldName] = '/Date(' + date.getTime() + ')/';
                        } catch (e) {
                            //TODO: Handle incorrect/different date formats
                            this._logWarn('Date format is incorrect for field ' + fieldName + ': ' + dateVal);
                            record[fieldName] = undefined;
                        }
                    } else {
                        this._logDebug('Date is empty for ' + fieldName);
                        record[fieldName] = undefined; //TODO: undefined, null or empty string?
                    }
                } else if (field.options && field.type == 'radiobutton') {
                    var $checkedElement = $inputElement.filter(':checked');
                    if ($checkedElement.length) {
                        record[fieldName] = $checkedElement.val();
                    } else {
                        record[fieldName] = undefined;
                    }
                } else {
                    record[fieldName] = $inputElement.val();
                }
            }
        },

        /* Sets enabled/disabled state of a dialog button.
        *************************************************************************/
        _setEnabledOfDialogButton: function ($button, enabled, buttonText) {
            if (!$button) {
                return;
            }

            if (enabled != false) {
                $button
                    .removeAttr('disabled')
                    .removeClass('ui-state-disabled');
            } else {
                $button
                    .attr('disabled', 'disabled')
                    .addClass('ui-state-disabled');
            }

            if (buttonText) {
                $button
                    .find('span')
                    .text(buttonText);
            }
        }

    });

})(jQuery);


/************************************************************************
* CREATE RECORD extension for jTable                                    *
*************************************************************************/
(function ($) {

    //Reference to base object members
    var base = {
        _create: $.hik.jtable.prototype._create
    };

    //extension members
    $.extend(true, $.hik.jtable.prototype, {

        /************************************************************************
        * DEFAULT OPTIONS / EVENTS                                              *
        *************************************************************************/
        options: {

            //Events
            recordAdded: function (event, data) { },

            //Localization
            messages: {
                addNewRecord: 'Add new record'
            }
        },

        /************************************************************************
        * PRIVATE FIELDS                                                        *
        *************************************************************************/

        _$addRecordDiv: null, //Reference to the adding new record dialog div (jQuery object)

        /************************************************************************
        * CONSTRUCTOR                                                           *
        *************************************************************************/

        /* Overrides base method to do create-specific constructions.
        *************************************************************************/
        _create: function () {
            base._create.apply(this, arguments);

            if (!this.options.actions.createAction) {
                return;
            }

            this._createAddRecordDialogDiv();
        },

        /* Creates and prepares add new record dialog div
        *************************************************************************/
        _createAddRecordDialogDiv: function () {
            var self = this;

            //Create a div for dialog and add to container element
            self._$addRecordDiv = $('<div />')
                .appendTo(self._$mainContainer);

            //Prepare dialog
            self._$addRecordDiv.dialog({
                autoOpen: false,
                //appendTo: "#navBars",
                show: self.options.dialogShowEffect,
                hide: self.options.dialogHideEffect,
                //width: 360,
                minWidth: 400,
                //height: "auto",
                dialogClass: "edit_order_dialog",
                //position: { my: "center center", at: "center center" },
                // maxHeight: 400,
                modal: true,
                title: self.options.messages.addNewRecord,
                buttons:
                        [{ //Cancel button
                            text: self.options.messages.cancel,
                            click: function () {
                                self._$addRecordDiv.dialog('close');
                            }
                        }, { //Save button
                            id: 'AddRecordDialogSaveButton',
                            text: self.options.messages.save,
                            click: function () {
                                self._onSaveClickedOnCreateForm();
                            }
                        }],
                close: function () {
                    var $addRecordForm = self._$addRecordDiv.find('form').first();
                    var $saveButton = self._$addRecordDiv.parent().find('#AddRecordDialogSaveButton');
                    self._trigger("formClosed", null, { form: $addRecordForm, formType: 'create' });
                    self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save);
                    $addRecordForm.remove();
                },
                open: function( event, ui ) {
                    var height = $( window ).height(),
                    dialog     = $( this ).closest( "div.ui-dialog" ).appendTo( "#navBars" );
                   
                    $( this ).height( height / 2 * 1.5 );
                    dialog.offset({ top: (height - dialog.height()) / 2 - 10 });
                }
            });

            if (self.options.addRecordButton) {
                //If user supplied a button, bind the click event to show dialog form
                self.options.addRecordButton.click(function (e) {
                    e.preventDefault();
                    self._showAddRecordForm();
                });
            } else {
                //If user did not supplied a button, create a 'add record button' toolbar item.
                self._addToolBarItem({
                    icon: true,
                    cssClass: 'jtable-toolbar-item-add-record',
                    text: self.options.messages.addNewRecord,
                    click: function () {
                        self._showAddRecordForm();
                    }
                });
            }
        },

        _onSaveClickedOnCreateForm: function () {
            var self = this;

            var $saveButton = self._$addRecordDiv.parent().find('#AddRecordDialogSaveButton');
            var $addRecordForm = self._$addRecordDiv.find('form');

            if (self._trigger("formSubmitting", null, { form: $addRecordForm, formType: 'create' }) != false) {
                self._setEnabledOfDialogButton($saveButton, false, self.options.messages.saving);
                self._saveAddRecordForm($addRecordForm, $saveButton);
            }
        },

        /************************************************************************
        * PUBLIC METHODS                                                        *
        *************************************************************************/

        /* Shows add new record dialog form.
        *************************************************************************/
        showCreateForm: function () {
            this._showAddRecordForm();
        },

        /* Adds a new record to the table (optionally to the server also)
        *************************************************************************/
        addRecord: function (options) {
            var self = this;
            options = $.extend({
                clientOnly: false,
                index: this._$tableRows.length,
                animationsEnabled: self.options.animationsEnabled,
                success: function () { },
                error: function () { }
            }, options);

            if (!options.record) {
                self._logWarn('options parameter in addRecord method must contain a record property.');
                return;
            }

            if (options.clientOnly) {
                self._addRow(
                    self._createRowFromRecord(options.record), {
                        isNewRow: true,
                        animationsEnabled: options.animationsEnabled,
                        clientOnly: options.clientOnly,
                        index: options.index
                    });

                options.success();
                return;
            }

            var completeAddRecord = function (data) {
                if (data.Result != 'OK') {
                    self._showError(data.Message);
                    options.error(data);
                    return;
                }

                if (!data.Record) {
                    self._logError('Server must return the created Record object.');
                    options.error(data);
                    return;
                }

                self._onRecordAdded(data);
                self._addRow(
                    self._createRowFromRecord(data.Record), {
                        isNewRow: true,
                        animationsEnabled: options.animationsEnabled,
                        clientOnly: options.clientOnly,
                        index: options.index
                    });

                options.success(data);
            };

            //createAction may be a function, check if it is
            if (!options.url && $.isFunction(self.options.actions.createAction)) {

                //Execute the function
                var funcResult = self.options.actions.createAction($.param(options.record));

                //Check if result is a jQuery Deferred object
                if (self._isDeferredObject(funcResult)) {
                    //Wait promise
                    funcResult.done(function (data) {
                        completeAddRecord(data);
                    }).fail(function () {
                        self._showError(self.options.messages.serverCommunicationError);
                        options.error();
                    });
                } else { //assume it returned the creation result
                    completeAddRecord(funcResult);
                }

            } else { //Assume it's a URL string

                //Make an Ajax call to create record
                self._submitFormUsingAjax(
                    options.url || self.options.actions.createAction,
                    $.param(options.record),
                    function (data) {
                        completeAddRecord(data);
                    },
                    function () {
                        self._showError(self.options.messages.serverCommunicationError);
                        options.error();
                    });

            }
        },

        /************************************************************************
        * PRIVATE METHODS                                                       *
        *************************************************************************/

        /* Shows add new record dialog form.
        *************************************************************************/
        _showAddRecordForm: function () {
            var self = this;

            //Create add new record form
            var $addRecordForm = $('<form id="jtable-create-form" class="jtable-dialog-form jtable-create-form"></form>');

            //Create input elements
            for (var i = 0; i < self._fieldList.length; i++) {

                var fieldName = self._fieldList[i];
                var field = self.options.fields[fieldName];

                //Do not create input for fields that is key and not specially marked as creatable
                if (field.key == true && field.create != true) {
                    continue;
                }

                //Do not create input for fields that are not creatable
                if (field.create == false) {
                    continue;
                }

                if (field.type == 'hidden') {
                    $addRecordForm.append(self._createInputForHidden(fieldName, field.defaultValue));
                    continue;
                }

                //Create a container div for this input field and add to form
                var $fieldContainer = $('<div />')
                    .addClass('jtable-input-field-container')
                    .appendTo($addRecordForm);

                //Create a label for input
                $fieldContainer.append(self._createInputLabelForRecordField(fieldName));

                //Create input element
                $fieldContainer.append(
                    self._createInputForRecordField({
                        fieldName: fieldName,
                        formType: 'create',
                        form: $addRecordForm
                    }));
            }

            self._makeCascadeDropDowns($addRecordForm, undefined, 'create');

            $addRecordForm.submit(function () {
                self._onSaveClickedOnCreateForm();
                return false;
            });

            //Open the form
            self._$addRecordDiv.append($addRecordForm).dialog('open');
            self._trigger("formCreated", null, { form: $addRecordForm, formType: 'create' });
        },

        /* Saves new added record to the server and updates table.
        *************************************************************************/
        _saveAddRecordForm: function ($addRecordForm, $saveButton) {
            var self = this;

            var completeAddRecord = function (data) {
                if (data.Result != 'OK') {
                    self._showError(data.Message);
                    self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save);
                    return;
                }

                if (!data.Record) {
                    self._logError('Server must return the created Record object.');
                    self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save);
                    return;
                }

                self._onRecordAdded(data);
                self._addRow(
                    self._createRowFromRecord(data.Record), {
                        isNewRow: true,
                        animationsEnabled: true,
                        clientOnly: true,
                        index: 0
                    });
                self._$addRecordDiv.dialog("close");

                // Activate Bootstrap Multiselect on new Row
                var new_row = $( "#jTable" ).find( "tr.jtable-data-row" ).eq(0);
                new_row.find( "select" ).multiselect({ buttonClass: 'btn btn-xs btn-info' });
            };

            $addRecordForm.data('submitting', true); //TODO: Why it's used, can remove? Check it.

            //createAction may be a function, check if it is
            if ($.isFunction(self.options.actions.createAction)) {

                //Execute the function
                var funcResult = self.options.actions.createAction($addRecordForm.serialize());

                //Check if result is a jQuery Deferred object
                if (self._isDeferredObject(funcResult)) {
                    //Wait promise
                    funcResult.done(function (data) {
                        completeAddRecord(data);
                    }).fail(function () {
                        self._showError(self.options.messages.serverCommunicationError);
                        self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save);
                    });
                } else { //assume it returned the creation result
                    completeAddRecord(funcResult);
                }

            } else { //Assume it's a URL string

                //Make an Ajax call to create record
                self._submitFormUsingAjax(
                    self.options.actions.createAction,
                    $addRecordForm.serialize(),
                    function (data) {
                        completeAddRecord(data);
                    },
                    function () {
                        self._showError(self.options.messages.serverCommunicationError);
                        self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save);
                    });
            }
        },

        _onRecordAdded: function (data) {
            this._trigger("recordAdded", null, { record: data.Record, serverResponse: data });
        }

    });

})(jQuery);


/************************************************************************
* EDIT RECORD extension for jTable                                      *
*************************************************************************/
(function ($) {

    //Reference to base object members
    var base = {
        _create: $.hik.jtable.prototype._create,
        _addColumnsToHeaderRow: $.hik.jtable.prototype._addColumnsToHeaderRow,
        _addCellsToRowUsingRecord: $.hik.jtable.prototype._addCellsToRowUsingRecord
    };

    //extension members
    $.extend(true, $.hik.jtable.prototype, {

        /************************************************************************
        * DEFAULT OPTIONS / EVENTS                                              *
        *************************************************************************/
        options: {

            //Events
            recordUpdated: function (event, data) { },
            rowUpdated: function (event, data) { },

            //Localization
            messages: {
                editRecord: 'Edit Record'
            }
        },

        /************************************************************************
        * PRIVATE FIELDS                                                        *
        *************************************************************************/

        _$editDiv: null, //Reference to the editing dialog div (jQuery object)
        _$editingRow: null, //Reference to currently editing row (jQuery object)

        /************************************************************************
        * CONSTRUCTOR AND INITIALIZATION METHODS                                *
        *************************************************************************/

        /* Overrides base method to do editing-specific constructions.
        *************************************************************************/
        _create: function () {
            base._create.apply(this, arguments);
            
            if (!this.options.actions.updateAction) {
                return;
            }
            
            this._createEditDialogDiv();
        },

        /* Creates and prepares edit dialog div
        *************************************************************************/
        _createEditDialogDiv: function () {
            var self = this;

            //Create a div for dialog and add to container element
            self._$editDiv = $('<div></div>')
                .appendTo(self._$mainContainer);

            //Prepare dialog
            self._$editDiv.dialog({
                autoOpen: false,
                show: self.options.dialogShowEffect,
                hide: self.options.dialogHideEffect,
                //width: 'auto',
                minWidth: 400,
                dialogClass: "edit_order_dialog",
                modal: true,
                title: self.options.messages.editRecord,
                buttons:
                        [{  //cancel button
                            text: self.options.messages.cancel,
                            click: function () {
                                self._$editDiv.dialog('close');
                            }
                        }, { //save button
                            id: 'EditDialogSaveButton',
                            text: self.options.messages.save,
                            click: function () {
                                self._onSaveClickedOnEditForm();
                            }
                        }],
                close: function () {
                    var $editForm = self._$editDiv.find('form:first');
                    var $saveButton = self._$editDiv.parent().find('#EditDialogSaveButton');
                    self._trigger("formClosed", null, { form: $editForm, formType: 'edit', row: self._$editingRow });
                    self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save);
                    $editForm.remove();
                },
                open: function( event, ui ) {
                    var height = $( window ).height(),
                    dialog     = $( this ).closest( "div.ui-dialog" ).appendTo( "#navBars" );
                   
                    $( this ).height( height / 2 * 1.5 );
                    dialog.offset({ top: (height - dialog.height()) / 2 - 10 });
                }
            });
        },

        /* Saves editing form to server.
        *************************************************************************/
        _onSaveClickedOnEditForm: function () {
            var self = this;
            
            //row maybe removed by another source, if so, do nothing
            if (self._$editingRow.hasClass('jtable-row-removed')) {
                self._$editDiv.dialog('close');
                return;
            }

            var $saveButton = self._$editDiv.parent().find('#EditDialogSaveButton');
            var $editForm = self._$editDiv.find('form');
            if (self._trigger("formSubmitting", null, { form: $editForm, formType: 'edit', row: self._$editingRow }) != false) {
                self._setEnabledOfDialogButton($saveButton, false, self.options.messages.saving);
                self._saveEditForm($editForm, $saveButton);
            }
        },

        /************************************************************************
        * PUBLIC METHODS                                                        *
        *************************************************************************/

        /* Updates a record on the table (optionally on the server also)
        *************************************************************************/
        updateRecord: function (options) {
            var self = this;
            options = $.extend({
                clientOnly: false,
                animationsEnabled: self.options.animationsEnabled,
                success: function () { },
                error: function () { }
            }, options);

            if (!options.record) {
                self._logWarn('options parameter in updateRecord method must contain a record property.');
                return;
            }

            var key = self._getKeyValueOfRecord(options.record);
            if (key == undefined || key == null) {
                self._logWarn('options parameter in updateRecord method must contain a record that contains the key field property.');
                return;
            }

            var $updatingRow = self.getRowByKey(key);
            if ($updatingRow == null) {
                self._logWarn('Can not found any row by key "' + key + '" on the table. Updating row must be visible on the table.');
                return;
            }

            if (options.clientOnly) {
                $.extend($updatingRow.data('record'), options.record);
                self._updateRowTexts($updatingRow);
                self._onRecordUpdated($updatingRow, null);
                if (options.animationsEnabled) {
                    self._showUpdateAnimationForRow($updatingRow);
                }

                options.success();
                return;
            }

            var completeEdit = function (data) {
                if (data.Result != 'OK') {
                    self._showError(data.Message);
                    options.error(data);
                    return;
                }

                $.extend($updatingRow.data('record'), options.record);
                self._updateRecordValuesFromServerResponse($updatingRow.data('record'), data);

                self._updateRowTexts($updatingRow);
                self._onRecordUpdated($updatingRow, data);
                if (options.animationsEnabled) {
                    self._showUpdateAnimationForRow($updatingRow);
                }

                options.success(data);
            };

            //updateAction may be a function, check if it is
            if (!options.url && $.isFunction(self.options.actions.updateAction)) {

                //Execute the function
                var funcResult = self.options.actions.updateAction($.param(options.record));

                //Check if result is a jQuery Deferred object
                if (self._isDeferredObject(funcResult)) {
                    //Wait promise
                    funcResult.done(function (data) {
                        completeEdit(data);
                    }).fail(function () {
                        self._showError(self.options.messages.serverCommunicationError);
                        options.error();
                    });
                } else { //assume it returned the creation result
                    completeEdit(funcResult);
                }

            } else { //Assume it's a URL string

                //Make an Ajax call to create record
                self._submitFormUsingAjax(
                    options.url || self.options.actions.updateAction,
                    $.param(options.record),
                    function (data) {
                        completeEdit(data);
                    },
                    function () {
                        self._showError(self.options.messages.serverCommunicationError);
                        options.error();
                    });

            }
        },

        /************************************************************************
        * OVERRIDED METHODS                                                     *
        *************************************************************************/

        /* Overrides base method to add a 'editing column cell' to header row.
        *************************************************************************/
        _addColumnsToHeaderRow: function ($tr) {
            base._addColumnsToHeaderRow.apply(this, arguments);
            if (this.options.actions.updateAction != undefined) {
                $tr.append(this._createEmptyCommandHeader());
            }
        },

        /* Overrides base method to add a 'edit command cell' to a row.
        *************************************************************************/
        _addCellsToRowUsingRecord: function ($row) {
            var self = this;
            base._addCellsToRowUsingRecord.apply(this, arguments);

            if (self.options.actions.updateAction != undefined) {
                var $span = $('<span></span>').html(self.options.messages.editRecord);
                var $button = $('<button title="' + self.options.messages.editRecord + '"></button>')
                    .addClass('jtable-command-button jtable-edit-command-button tooltip_set')
                    .append($span)
                    .click(function (e) {
                        e.preventDefault();
                        e.stopPropagation();
                        self._showEditForm($row);
                    });
                $('<td></td>')
                    .addClass('jtable-command-column')
                    .append($button)
                    .appendTo($row);
            }
        },

        /************************************************************************
        * PRIVATE METHODS                                                       *
        *************************************************************************/

        /* Shows edit form for a row.
        *************************************************************************/
        _showEditForm: function ($tableRow) {
            var self = this;
            var record = $tableRow.data('record');

            //Create edit form
            var $editForm = $('<form id="jtable-edit-form" class="jtable-dialog-form jtable-edit-form"></form>');

            //Create input fields
            for (var i = 0; i < self._fieldList.length; i++) {

                var fieldName = self._fieldList[i];
                var field = self.options.fields[fieldName];
                var fieldValue = record[fieldName];

                if (field.key == true) {
                    if (field.edit != true) {
                        //Create hidden field for key
                        $editForm.append(self._createInputForHidden(fieldName, fieldValue));
                        continue;
                    } else {
                        //Create a special hidden field for key (since key is be editable)
                        $editForm.append(self._createInputForHidden('jtRecordKey', fieldValue));
                    }
                }

                //Do not create element for non-editable fields
                if (field.edit == false) {
                    continue;
                }

                //Hidden field
                if (field.type == 'hidden') {
                    $editForm.append(self._createInputForHidden(fieldName, fieldValue));
                    continue;
                }

                //Create a container div for this input field and add to form
                var $fieldContainer = $('<div class="jtable-input-field-container"></div>').appendTo($editForm);

                //Create a label for input
                $fieldContainer.append(self._createInputLabelForRecordField(fieldName));

                //Create input element with it's current value
                var currentValue = self._getValueForRecordField(record, fieldName);
                $fieldContainer.append(
                    self._createInputForRecordField({
                        fieldName: fieldName,
                        value: currentValue,
                        record: record,
                        formType: 'edit',
                        form: $editForm
                    }));
            }
            
            self._makeCascadeDropDowns($editForm, record, 'edit');

            $editForm.submit(function () {
                self._onSaveClickedOnEditForm();
                return false;
            });

            //Open dialog
            self._$editingRow = $tableRow;
            self._$editDiv.append($editForm).dialog('open');
            self._trigger("formCreated", null, { form: $editForm, formType: 'edit', record: record, row: $tableRow });
        },

        /* Saves editing form to the server and updates the record on the table.
        *************************************************************************/
        _saveEditForm: function ($editForm, $saveButton) {
            var self = this;
            
            var completeEdit = function (data) {
                if (data.Result != 'OK') {
                    self._showError(data.Message);
                    self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save);
                    return;
                }

                var record = self._$editingRow.data('record');

                self._updateRecordValuesFromForm(record, $editForm);
                self._updateRecordValuesFromServerResponse(record, data);
                self._updateRowTexts(self._$editingRow);

                self._$editingRow.attr('data-record-key', self._getKeyValueOfRecord(record));

                self._onRecordUpdated(self._$editingRow, data);

                if (self.options.animationsEnabled) {
                    self._showUpdateAnimationForRow(self._$editingRow);
                }

                self._$editDiv.dialog("close");
            };


            //updateAction may be a function, check if it is
            if ($.isFunction(self.options.actions.updateAction)) {

                //Execute the function
                var funcResult = self.options.actions.updateAction($editForm.serialize());

                //Check if result is a jQuery Deferred object
                if (self._isDeferredObject(funcResult)) {
                    //Wait promise
                    funcResult.done(function (data) {
                        completeEdit(data);
                    }).fail(function () {
                        self._showError(self.options.messages.serverCommunicationError);
                        self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save);
                    });
                } else { //assume it returned the creation result
                    completeEdit(funcResult);
                }

            } else { //Assume it's a URL string

                //Make an Ajax call to update record
                self._submitFormUsingAjax(
                    self.options.actions.updateAction,
                    $editForm.serialize(),
                    function(data) {
                        completeEdit(data);
                    },
                    function() {
                        self._showError(self.options.messages.serverCommunicationError);
                        self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save);
                    });
            }

        },

        /* This method ensures updating of current record with server response,
        * if server sends a Record object as response to updateAction.
        *************************************************************************/
        _updateRecordValuesFromServerResponse: function (record, serverResponse) {
            if (!serverResponse || !serverResponse.Record) {
                return;
            }

            $.extend(true, record, serverResponse.Record);
        },

        /* Gets text for a field of a record according to it's type.
        *************************************************************************/
        _getValueForRecordField: function (record, fieldName) {
            var field = this.options.fields[fieldName];
            var fieldValue = record[fieldName];
            if (field.type == 'date') {
                return this._getDisplayTextForDateRecordField(field, fieldValue);
            } else {
                return fieldValue;
            }
        },

        /* Updates cells of a table row's text values from row's record values.
        *************************************************************************/
        _updateRowTexts: function ($tableRow) {
            var record = $tableRow.data('record');
            var $columns = $tableRow.find('td');
            for (var i = 0; i < this._columnList.length; i++) {
                var displayItem = this._getDisplayTextForRecordField(record, this._columnList[i]);
                if ((displayItem != "") && (displayItem == 0)) displayItem = "0";
                $columns.eq(this._firstDataColumnOffset + i).html(displayItem || '');
            }

            this._onRowUpdated($tableRow);
        },

        /* Shows 'updated' animation for a table row.
        *************************************************************************/
        _showUpdateAnimationForRow: function ($tableRow) {
            var className = 'jtable-row-updated';
            if (this.options.jqueryuiTheme) {
                className = className + ' ui-state-highlight';
            }

            $tableRow.stop(true, true).addClass(className, 'slow', '', function () {
                $tableRow.removeClass(className, 5000);
            });
        },

        /************************************************************************
        * EVENT RAISING METHODS                                                 *
        *************************************************************************/

        _onRowUpdated: function ($row) {
            this._trigger("rowUpdated", null, { row: $row, record: $row.data('record') });
        },

        _onRecordUpdated: function ($row, data) {
            this._trigger("recordUpdated", null, { record: $row.data('record'), row: $row, serverResponse: data });
        }

    });

})(jQuery);


/************************************************************************
* DELETION extension for jTable                                         *
*************************************************************************/
(function ($) {

    //Reference to base object members
    var base = {
        _create: $.hik.jtable.prototype._create,
        _addColumnsToHeaderRow: $.hik.jtable.prototype._addColumnsToHeaderRow,
        _addCellsToRowUsingRecord: $.hik.jtable.prototype._addCellsToRowUsingRecord
    };

    //extension members
    $.extend(true, $.hik.jtable.prototype, {

        /************************************************************************
        * DEFAULT OPTIONS / EVENTS                                              *
        *************************************************************************/
        options: {

            //Options
            deleteConfirmation: true,
            clientOnly: true,

            //Events
            recordDeleted: function (event, data) { },

            //Localization
            messages: {
                deleteConfirmation: 'This record will be deleted. Are you sure?',
                deleteText: 'Delete',
                deleting: 'Deleting',
                canNotDeletedRecords: 'Can not delete {0} of {1} records!',
                deleteProggress: 'Deleting {0} of {1} records, processing...'
            }
        },

        /************************************************************************
        * PRIVATE FIELDS                                                        *
        *************************************************************************/

        _$deleteRecordDiv: null, //Reference to the adding new record dialog div (jQuery object)
        _$deletingRow: null, //Reference to currently deleting row (jQuery object)

        /************************************************************************
        * CONSTRUCTOR                                                           *
        *************************************************************************/

        /* Overrides base method to do deletion-specific constructions.
        *************************************************************************/
        _create: function () {
            base._create.apply(this, arguments);
            this._createDeleteDialogDiv();
        },

        /* Creates and prepares delete record confirmation dialog div.
        *************************************************************************/
        _createDeleteDialogDiv: function () {
            var self = this;

            //Check if deleteAction is supplied
            if (!self.options.actions.deleteAction) {
                return;
            }

            //Create div element for delete confirmation dialog
            self._$deleteRecordDiv = $('<div><p><span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 20px 0;"></span><span class="jtable-delete-confirm-message"></span></p></div>').appendTo(self._$mainContainer);

            //Prepare dialog
            self._$deleteRecordDiv.dialog({
                autoOpen: false,
                show: self.options.dialogShowEffect,
                hide: self.options.dialogHideEffect,
                modal: true,
                dialogClass: "edit_order_dialog",
                title: self.options.messages.areYouSure,
                buttons:
                        [{  //cancel button
                            text: self.options.messages.cancel,
                            click: function () {
                                self._$deleteRecordDiv.dialog("close");
                            }
                        }, {//delete button
                            id: 'DeleteDialogButton',
                            text: self.options.messages.deleteText,
                            click: function () {

                                //row maybe removed by another source, if so, do nothing
                                if (self._$deletingRow.hasClass('jtable-row-removed')) {
                                    self._$deleteRecordDiv.dialog('close');
                                    return;
                                }

                                var $deleteButton = self._$deleteRecordDiv.parent().find('#DeleteDialogButton');
                                self._setEnabledOfDialogButton($deleteButton, false, self.options.messages.deleting);
                                self._deleteRecordFromServer(
                                    self._$deletingRow,
                                    function () {
                                        self._removeRowsFromTableWithAnimation(self._$deletingRow, false, self.options.clientOnly);
                                        self._$deleteRecordDiv.dialog('close');
                                    },
                                    function (message) { //error
                                        self._showError(message);
                                        self._setEnabledOfDialogButton($deleteButton, true, self.options.messages.deleteText);
                                    }
                                );
                            }
                        }],
                close: function () {
                    var $deleteButton = self._$deleteRecordDiv.parent().find('#DeleteDialogButton');
                    self._setEnabledOfDialogButton($deleteButton, true, self.options.messages.deleteText);
                },
                open: function( event, ui ) {
                    var height = $( window ).height(),
                    dialog     = $( this ).closest( "div.ui-dialog" ).appendTo( "#navBars" );
                   
                    //$( this ).height( height / 2 * 1.5 );
                    dialog.offset({ top: (height - dialog.height()) / 2 - 10 });
                }
            });
        },

        /************************************************************************
        * PUBLIC METHODS                                                        *
        *************************************************************************/

        /* This method is used to delete one or more rows from server and the table.
        *************************************************************************/
        deleteRows: function ($rows, options) {
            var self = this;

            if ($rows.length <= 0) {
                self._logWarn('No rows specified to jTable deleteRows method.');
                return;
            }

            if (self._isBusy()) {
                self._logWarn('Can not delete rows since jTable is busy!');
                return;
            }

            //Deleting just one row
            if ($rows.length == 1) {
                self._deleteRecordFromServer(
                    $rows,
                    function () { //success
                        self._removeRowsFromTableWithAnimation($rows, false, self.options.clientOnly);
                    },
                    function (message) { //error
                        self._showError(message);
                    }
                );

                return;
            }

            //Deleting multiple rows
            self._showBusy(self._formatString(self.options.messages.deleteProggress, 0, $rows.length));

            //This method checks if deleting of all records is completed
            var completedCount = 0;
            var isCompleted = function () {
                return (completedCount >= $rows.length);
            };

            //This method is called when deleting of all records completed
            var completed = function () {
                var $deletedRows = $rows.filter('.jtable-row-ready-to-remove');
                if ($deletedRows.length < $rows.length) {
                    self._showError(self._formatString(self.options.messages.canNotDeletedRecords, $rows.length - $deletedRows.length, $rows.length));
                }

                if ($deletedRows.length > 0) {
                    self._removeRowsFromTableWithAnimation($deletedRows, false, self.options.clientOnly);
                }

                self._hideBusy();
            };

            //Delete all rows
            var deletedCount = 0;
            $rows.each(function () {
                var $row = $(this);
                self._deleteRecordFromServer(
                    $row,
                    function () { //success
                        ++deletedCount; ++completedCount;
                        $row.addClass('jtable-row-ready-to-remove');
                        self._showBusy(self._formatString(self.options.messages.deleteProggress, deletedCount, $rows.length));
                        if (isCompleted()) {
                            completed();
                        }
                    },
                    function () { //error
                        ++completedCount;
                        if (isCompleted()) {
                            completed();
                        }
                    }
                );
            });
        },

        /* Deletes a record from the table (optionally from the server also).
        *************************************************************************/
        deleteRecord: function (options) {
            var self = this;
            options = $.extend({
                clientOnly: false,
                animationsEnabled: self.options.animationsEnabled,
                url: self.options.actions.deleteAction,
                success: function () { },
                error: function () { }
            }, options);

            if (options.key == undefined) {
                self._logWarn('options parameter in deleteRecord method must contain a key property.');
                return;
            }

            var $deletingRow = self.getRowByKey(options.key);
            if ($deletingRow == null) {
                self._logWarn('Can not found any row by key: ' + options.key);
                return;
            }

            if (options.clientOnly) {
                self._removeRowsFromTableWithAnimation($deletingRow, options.animationsEnabled, options.clientOnly);
                options.success();
                return;
            }

            self._deleteRecordFromServer(
                    $deletingRow,
                    function (data) { //success
                        self._removeRowsFromTableWithAnimation($deletingRow, options.animationsEnabled, options.clientOnly);
                        options.success(data);
                    },
                    function (message) { //error
                        self._showError(message);
                        options.error(message);
                    },
                    options.url
                );
        },

        /************************************************************************
        * OVERRIDED METHODS                                                     *
        *************************************************************************/

        /* Overrides base method to add a 'deletion column cell' to header row.
        *************************************************************************/
        _addColumnsToHeaderRow: function ($tr) {
            base._addColumnsToHeaderRow.apply(this, arguments);
            if (this.options.actions.deleteAction != undefined) {
                $tr.append(this._createEmptyCommandHeader());
            }
        },

        /* Overrides base method to add a 'delete command cell' to a row.
        *************************************************************************/
        _addCellsToRowUsingRecord: function ($row) {
            base._addCellsToRowUsingRecord.apply(this, arguments);

            var self = this;
            if (self.options.actions.deleteAction != undefined) {
                var $span = $('<span></span>').html(self.options.messages.deleteText);
                var $button = $('<button data-placement="left" title="' + self.options.messages.deleteText + '"></button>')
                    .addClass('jtable-command-button jtable-delete-command-button tooltip_set')
                    .append($span)
                    .click(function (e) {
                        e.preventDefault();
                        e.stopPropagation();
                        self._deleteButtonClickedForRow($row);
                    });
                $('<td></td>')
                    .addClass('jtable-command-column')
                    .append($button)
                    .appendTo($row);
            }
        },

        /************************************************************************
        * PRIVATE METHODS                                                       *
        *************************************************************************/

        /* This method is called when user clicks delete button on a row.
        *************************************************************************/
        _deleteButtonClickedForRow: function ($row) {
            var self = this;

            var deleteConfirm;
            var deleteConfirmMessage = self.options.messages.deleteConfirmation;

            //If options.deleteConfirmation is function then call it
            if ($.isFunction(self.options.deleteConfirmation)) {
                var data = { row: $row, record: $row.data('record'), deleteConfirm: true, deleteConfirmMessage: deleteConfirmMessage, cancel: false, cancelMessage: null };
                self.options.deleteConfirmation(data);

                //If delete progress is cancelled
                if (data.cancel) {

                    //If a canlellation reason is specified
                    if (data.cancelMessage) {
                        self._showError(data.cancelMessage); //TODO: show warning/stop message instead of error (also show warning/error ui icon)!
                    }

                    return;
                }

                deleteConfirmMessage = data.deleteConfirmMessage;
                deleteConfirm = data.deleteConfirm;
            } else {
                deleteConfirm = self.options.deleteConfirmation;
            }

            if (deleteConfirm != false) {
                //Confirmation
                self._$deleteRecordDiv.find('.jtable-delete-confirm-message').html(deleteConfirmMessage);
                self._showDeleteDialog($row);
            } else {
                //No confirmation
                self._deleteRecordFromServer(
                    $row,
                    function () { //success
                        self._removeRowsFromTableWithAnimation($row, false, self.options.clientOnly);
                    },
                    function (message) { //error
                        self._showError(message);
                    }
                );
            }
        },

        /* Shows delete comfirmation dialog.
        *************************************************************************/
        _showDeleteDialog: function ($row) {
            this._$deletingRow = $row;
            this._$deleteRecordDiv.dialog('open');
        },

        /* Performs an ajax call to server to delete record
        *  and removes row of the record from table if ajax call success.
        *************************************************************************/
        _deleteRecordFromServer: function ($row, success, error, url) {
            var self = this;

            var completeDelete = function(data) {
                if (data.Result != 'OK') {
                    $row.data('deleting', false);
                    if (error) {
                        error(data.Message);
                    }

                    return;
                }

                self._trigger("recordDeleted", null, { record: $row.data('record'), row: $row, serverResponse: data });

                if (success) {
                    success(data);
                }
            };

            //Check if it is already being deleted right now
            if ($row.data('deleting') == true) {
                return;
            }

            $row.data('deleting', true);

            var postData = {};
            postData[self._keyField] = self._getKeyValueOfRecord($row.data('record'));
            
            //deleteAction may be a function, check if it is
            if (!url && $.isFunction(self.options.actions.deleteAction)) {

                //Execute the function
                var funcResult = self.options.actions.deleteAction(postData);

                //Check if result is a jQuery Deferred object
                if (self._isDeferredObject(funcResult)) {
                    //Wait promise
                    funcResult.done(function (data) {
                        completeDelete(data);
                    }).fail(function () {
                        $row.data('deleting', false);
                        if (error) {
                            error(self.options.messages.serverCommunicationError);
                        }
                    });
                } else { //assume it returned the deletion result
                    completeDelete(funcResult);
                }

            } else { //Assume it's a URL string
                //Make ajax call to delete the record from server
                this._ajax({
                    url: (url || self.options.actions.deleteAction),
                    data: postData,
                    success: function (data) {
                        completeDelete(data);
                    },
                    error: function () {
                        $row.data('deleting', false);
                        if (error) {
                            error(self.options.messages.serverCommunicationError);
                        }
                    }
                });

            }
        },

        /* Removes a row from table after a 'deleting' animation.
        *************************************************************************/
        _removeRowsFromTableWithAnimation: function ($rows, animationsEnabled, clientOnly) {
            var self = this;

            if (animationsEnabled == undefined) {
                animationsEnabled = self.options.animationsEnabled;
            }

            if (animationsEnabled) {
                var className = 'jtable-row-deleting';
                if (this.options.jqueryuiTheme) {
                    className = className + ' ui-state-disabled';
                }

                //Stop current animation (if does exists) and begin 'deleting' animation.
                $rows.stop(true, true).addClass(className, 'slow', '').promise().done(function () {
                    self._removeRowsFromTable($rows, 'deleted', clientOnly);
                });
            } else {
                self._removeRowsFromTable($rows, 'deleted', clientOnly);
            }
        }

    });

})(jQuery);


/************************************************************************
* SELECTING extension for jTable                                        *
*************************************************************************/
(function ($) {

    //Reference to base object members
    var base = {
        _create: $.hik.jtable.prototype._create,
        _addColumnsToHeaderRow: $.hik.jtable.prototype._addColumnsToHeaderRow,
        _addCellsToRowUsingRecord: $.hik.jtable.prototype._addCellsToRowUsingRecord,
        _onLoadingRecords: $.hik.jtable.prototype._onLoadingRecords,
        _onRecordsLoaded: $.hik.jtable.prototype._onRecordsLoaded,
        _onRowsRemoved: $.hik.jtable.prototype._onRowsRemoved
    };

    //extension members
    $.extend(true, $.hik.jtable.prototype, {

        /************************************************************************
        * DEFAULT OPTIONS / EVENTS                                              *
        *************************************************************************/
        options: {

            //Options
            selecting: false,
            multiselect: false,
            selectingCheckboxes: false,
            selectOnRowClick: true,

            //Events
            selectionChanged: function (event, data) { }
        },

        /************************************************************************
        * PRIVATE FIELDS                                                        *
        *************************************************************************/

        _selectedRecordIdsBeforeLoad: null, //This array is used to store selected row Id's to restore them after a page refresh (string array).
        _$selectAllCheckbox: null, //Reference to the 'select/deselect all' checkbox (jQuery object)
        _shiftKeyDown: false, //True, if shift key is currently down.

        /************************************************************************
        * CONSTRUCTOR                                                           *
        *************************************************************************/

        /* Overrides base method to do selecting-specific constructions.
        *************************************************************************/
        _create: function () {
            if (this.options.selecting && this.options.selectingCheckboxes) {
                ++this._firstDataColumnOffset;
                this._bindKeyboardEvents();
            }

            //Call base method
            base._create.apply(this, arguments);
        },

        /* Registers to keyboard events those are needed for selection
        *************************************************************************/
        _bindKeyboardEvents: function () {
            var self = this;
            //Register to events to set _shiftKeyDown value
            $(document)
                .keydown(function (event) {
                    switch (event.which) {
                        case 16:
                            self._shiftKeyDown = true;
                            break;
                    }
                })
                .keyup(function (event) {
                    switch (event.which) {
                        case 16:
                            self._shiftKeyDown = false;
                            break;
                    }
                });
        },

        /************************************************************************
        * PUBLIC METHODS                                                        *
        *************************************************************************/

        /* Gets jQuery selection for currently selected rows.
        *************************************************************************/
        selectedRows: function () {
            return this._getSelectedRows();
        },

        /* Makes row/rows 'selected'.
        *************************************************************************/
        selectRows: function ($rows) {
            this._selectRows($rows);
            this._onSelectionChanged(); //TODO: trigger only if selected rows changes?
        },

        /************************************************************************
        * OVERRIDED METHODS                                                     *
        *************************************************************************/

        /* Overrides base method to add a 'select column' to header row.
        *************************************************************************/
        _addColumnsToHeaderRow: function ($tr) {
            if (this.options.selecting && this.options.selectingCheckboxes) {
                if (this.options.multiselect) {
                    $tr.append(this._createSelectAllHeader());
                } else {
                    $tr.append(this._createEmptyCommandHeader());
                }
            }

            base._addColumnsToHeaderRow.apply(this, arguments);
        },

        /* Overrides base method to add a 'delete command cell' to a row.
        *************************************************************************/
        _addCellsToRowUsingRecord: function ($row) {
            if (this.options.selecting) {
                this._makeRowSelectable($row);
            }

            base._addCellsToRowUsingRecord.apply(this, arguments);
        },

        /* Overrides base event to store selection list
        *************************************************************************/
        _onLoadingRecords: function () {
            if (this.options.selecting) {
                this._storeSelectionList();
            }

            base._onLoadingRecords.apply(this, arguments);
        },

        /* Overrides base event to restore selection list
        *************************************************************************/
        _onRecordsLoaded: function () {
            if (this.options.selecting) {
                this._restoreSelectionList();
            }

            base._onRecordsLoaded.apply(this, arguments);
        },

        /* Overrides base event to check is any selected row is being removed.
        *************************************************************************/
        _onRowsRemoved: function ($rows, reason) {
            if (this.options.selecting && (reason != 'reloading') && ($rows.filter('.jtable-row-selected').length > 0)) {
                this._onSelectionChanged();
            }

            base._onRowsRemoved.apply(this, arguments);
        },

        /************************************************************************
        * PRIVATE METHODS                                                       *
        *************************************************************************/

        /* Creates a header column to select/deselect all rows.
        *************************************************************************/
        _createSelectAllHeader: function () {
            var self = this;

            var $columnHeader = $('<th class=""></th>')
                .addClass('jtable-command-column-header jtable-column-header-selecting');
            this._jqueryuiThemeAddClass($columnHeader, 'ui-state-default');

            var $headerContainer = $('<div />')
                .addClass('jtable-column-header-container')
                .appendTo($columnHeader);

            self._$selectAllCheckbox = $('<input type="checkbox" />')
                .appendTo($headerContainer)
                .click(function () {
                    if (self._$tableRows.length <= 0) {
                        self._$selectAllCheckbox.attr('checked', false);
                        return;
                    }

                    var allRows = self._$tableBody.find('>tr.jtable-data-row');
                    if (self._$selectAllCheckbox.is(':checked')) {
                        self._selectRows(allRows);
                    } else {
                        self._deselectRows(allRows);
                    }

                    self._onSelectionChanged();
                });

            return $columnHeader;
        },

        /* Stores Id's of currently selected records to _selectedRecordIdsBeforeLoad.
        *************************************************************************/
        _storeSelectionList: function () {
            var self = this;

            if (!self.options.selecting) {
                return;
            }

            self._selectedRecordIdsBeforeLoad = [];
            self._getSelectedRows().each(function () {
                self._selectedRecordIdsBeforeLoad.push(self._getKeyValueOfRecord($(this).data('record')));
            });
        },

        /* Selects rows whose Id is in _selectedRecordIdsBeforeLoad;
        *************************************************************************/
        _restoreSelectionList: function () {
            var self = this;

            if (!self.options.selecting) {
                return;
            }

            var selectedRowCount = 0;
            for (var i = 0; i < self._$tableRows.length; ++i) {
                var recordId = self._getKeyValueOfRecord(self._$tableRows[i].data('record'));
                if ($.inArray(recordId, self._selectedRecordIdsBeforeLoad) > -1) {
                    self._selectRows(self._$tableRows[i]);
                    ++selectedRowCount;
                }
            }

            if (self._selectedRecordIdsBeforeLoad.length > 0 && self._selectedRecordIdsBeforeLoad.length != selectedRowCount) {
                self._onSelectionChanged();
            }

            self._selectedRecordIdsBeforeLoad = [];
            self._refreshSelectAllCheckboxState();
        },

        /* Gets all selected rows.
        *************************************************************************/
        _getSelectedRows: function () {
            return this._$tableBody
                .find('>tr.jtable-row-selected');
        },

        /* Adds selectable feature to a row.
        *************************************************************************/
        _makeRowSelectable: function ($row) {
            var self = this;

            //Select/deselect on row click
            if (self.options.selectOnRowClick) {
                $row.click(function () {
                    self._invertRowSelection($row);
                });
            }

            //'select/deselect' checkbox column
            if (self.options.selectingCheckboxes) {
                var $cell = $('<td></td>').addClass('jtable-selecting-column');
                var $selectCheckbox = $('<input type="checkbox" />').appendTo($cell);
                if (!self.options.selectOnRowClick) {
                    $selectCheckbox.click(function () {
                        self._invertRowSelection($row);
                    });
                }

                $row.append($cell);
            }
        },

        /* Inverts selection state of a single row.
        *************************************************************************/
        _invertRowSelection: function ($row) {
            if ($row.hasClass('jtable-row-selected')) {
                this._deselectRows($row);
            } else {
                //Shift key?
                if (this._shiftKeyDown) {
                    var rowIndex = this._findRowIndex($row);
                    //try to select row and above rows until first selected row
                    var beforeIndex = this._findFirstSelectedRowIndexBeforeIndex(rowIndex) + 1;
                    if (beforeIndex > 0 && beforeIndex < rowIndex) {
                        this._selectRows(this._$tableBody.find('tr').slice(beforeIndex, rowIndex + 1));
                    } else {
                        //try to select row and below rows until first selected row
                        var afterIndex = this._findFirstSelectedRowIndexAfterIndex(rowIndex) - 1;
                        if (afterIndex > rowIndex) {
                            this._selectRows(this._$tableBody.find('tr').slice(rowIndex, afterIndex + 1));
                        } else {
                            //just select this row
                            this._selectRows($row);
                        }
                    }
                } else {
                    this._selectRows($row);
                }
            }

            this._onSelectionChanged();
        },

        /* Search for a selected row (that is before given row index) to up and returns it's index 
        *************************************************************************/
        _findFirstSelectedRowIndexBeforeIndex: function (rowIndex) {
            for (var i = rowIndex - 1; i >= 0; --i) {
                if (this._$tableRows[i].hasClass('jtable-row-selected')) {
                    return i;
                }
            }

            return -1;
        },

        /* Search for a selected row (that is after given row index) to down and returns it's index 
        *************************************************************************/
        _findFirstSelectedRowIndexAfterIndex: function (rowIndex) {
            for (var i = rowIndex + 1; i < this._$tableRows.length; ++i) {
                if (this._$tableRows[i].hasClass('jtable-row-selected')) {
                    return i;
                }
            }

            return -1;
        },

        /* Makes row/rows 'selected'.
        *************************************************************************/
        _selectRows: function ($rows) {
            if (!this.options.multiselect) {
                this._deselectRows(this._getSelectedRows());
            }

            $rows.addClass('jtable-row-selected');
            this._jqueryuiThemeAddClass($rows, 'ui-state-highlight');

            if (this.options.selectingCheckboxes) {
                $rows.find('>td.jtable-selecting-column >input').prop('checked', true);
            }

            this._refreshSelectAllCheckboxState();
        },

        /* Makes row/rows 'non selected'.
        *************************************************************************/
        _deselectRows: function ($rows) {
            $rows.removeClass('jtable-row-selected ui-state-highlight');
            if (this.options.selectingCheckboxes) {
                $rows.find('>td.jtable-selecting-column >input').prop('checked', false);
            }

            this._refreshSelectAllCheckboxState();
        },

        /* Updates state of the 'select/deselect' all checkbox according to count of selected rows.
        *************************************************************************/
        _refreshSelectAllCheckboxState: function () {
            if (!this.options.selectingCheckboxes || !this.options.multiselect) {
                return;
            }

            var totalRowCount = this._$tableRows.length;
            var selectedRowCount = this._getSelectedRows().length;

            if (selectedRowCount == 0) {
                this._$selectAllCheckbox.prop('indeterminate', false);
                this._$selectAllCheckbox.attr('checked', false);
            } else if (selectedRowCount == totalRowCount) {
                this._$selectAllCheckbox.prop('indeterminate', false);
                this._$selectAllCheckbox.attr('checked', true);
            } else {
                this._$selectAllCheckbox.attr('checked', false);
                this._$selectAllCheckbox.prop('indeterminate', true);
            }
        },

        /************************************************************************
        * EVENT RAISING METHODS                                                 *
        *************************************************************************/

        _onSelectionChanged: function () {
            this._trigger("selectionChanged", null, {});
        }

    });

})(jQuery);


/************************************************************************
* PAGING extension for jTable                                           *
*************************************************************************/
(function ($) {

    //Reference to base object members
    var base = {
        load: $.hik.jtable.prototype.load,
        _create: $.hik.jtable.prototype._create,
        _setOption: $.hik.jtable.prototype._setOption,
        _createRecordLoadUrl: $.hik.jtable.prototype._createRecordLoadUrl,
        _createJtParamsForLoading: $.hik.jtable.prototype._createJtParamsForLoading,
        _addRowToTable: $.hik.jtable.prototype._addRowToTable,
        _addRow: $.hik.jtable.prototype._addRow,
        _removeRowsFromTable: $.hik.jtable.prototype._removeRowsFromTable,
        _onRecordsLoaded: $.hik.jtable.prototype._onRecordsLoaded
    };

    //extension members
    $.extend(true, $.hik.jtable.prototype, {

        /************************************************************************
        * DEFAULT OPTIONS / EVENTS                                              *
        *************************************************************************/
        options: {
            paging: false,
            pageList: 'normal', //possible values: 'minimal', 'normal'
            pageSize: 10,
            pageSizes: [10, 25, 50, 100, 250, 500],
            pageSizeChangeArea: true,
            gotoPageArea: 'combobox', //possible values: 'textbox', 'combobox', 'none'

            messages: {
                pagingInfo: 'Showing {0}-{1} of {2}',
                pageSizeChangeLabel: 'Row count',
                gotoPageLabel: 'Go to page'
            }
        },

        /************************************************************************
        * PRIVATE FIELDS                                                        *
        *************************************************************************/

        _$bottomPanel: null, //Reference to the panel at the bottom of the table (jQuery object)
        _$pagingListArea: null, //Reference to the page list area in to bottom panel (jQuery object)
        _$pageSizeChangeArea: null, //Reference to the page size change area in to bottom panel (jQuery object)
        _$pageInfoSpan: null, //Reference to the paging info area in to bottom panel (jQuery object)
        _$gotoPageArea: null, //Reference to 'Go to page' input area in to bottom panel (jQuery object)
        _$gotoPageInput: null, //Reference to 'Go to page' input in to bottom panel (jQuery object)
        _totalRecordCount: 0, //Total count of records on all pages
        _currentPageNo: 1, //Current page number

        /************************************************************************
        * CONSTRUCTOR AND INITIALIZING METHODS                                  *
        *************************************************************************/

        /* Overrides base method to do paging-specific constructions.
        *************************************************************************/
        _create: function() {
            base._create.apply(this, arguments);
            if (this.options.paging) {
                this._loadPagingSettings();
                this._createBottomPanel();
                this._createPageListArea();
                this._createGotoPageInput();
                this._createPageSizeSelection();
            }
        },

        /* Loads user preferences for paging.
        *************************************************************************/
        _loadPagingSettings: function() {
            if (!this.options.saveUserPreferences) {
                return;
            }

            var pageSize = this._getCookie('page-size');
            if (pageSize) {
                this.options.pageSize = this._normalizeNumber(pageSize, 1, 1000000, this.options.pageSize);
            }
        },

        /* Creates bottom panel and adds to the page.
        *************************************************************************/
        _createBottomPanel: function() {
            this._$bottomPanel = $('<div />')
                .addClass('jtable-bottom-panel')
                .insertAfter(this._$table);

            this._jqueryuiThemeAddClass(this._$bottomPanel, 'ui-state-default');

            $('<div />').addClass('jtable-left-area').appendTo(this._$bottomPanel);
            $('<div />').addClass('jtable-right-area').appendTo(this._$bottomPanel);
        },

        /* Creates page list area.
        *************************************************************************/
        _createPageListArea: function() {
            this._$pagingListArea = $('<span></span>')
                .addClass('jtable-page-list')
                .appendTo(this._$bottomPanel.find('.jtable-left-area'));

            this._$pageInfoSpan = $('<span></span>')
                .addClass('jtable-page-info')
                .appendTo(this._$bottomPanel.find('.jtable-right-area'));
        },

        /* Creates page list change area.
        *************************************************************************/
        _createPageSizeSelection: function() {
            var self = this;

            if (!self.options.pageSizeChangeArea) {
                return;
            }

            //Add current page size to page sizes list if not contains it
            if (self._findIndexInArray(self.options.pageSize, self.options.pageSizes) < 0) {
                self.options.pageSizes.push(parseInt(self.options.pageSize));
                self.options.pageSizes.sort(function(a, b) { return a - b; });
            }

            //Add a span to contain page size change items
            self._$pageSizeChangeArea = $('<span></span>')
                .addClass('jtable-page-size-change')
                .appendTo(self._$bottomPanel.find('.jtable-left-area'));

            //Page size label
            self._$pageSizeChangeArea.append('<span>' + self.options.messages.pageSizeChangeLabel + ': </span>');

            //Page size change combobox
            var $pageSizeChangeCombobox = $('<select></select>').appendTo(self._$pageSizeChangeArea);

            //Add page sizes to the combobox
            for (var i = 0; i < self.options.pageSizes.length; i++) {
                $pageSizeChangeCombobox.append('<option value="' + self.options.pageSizes[i] + '">' + self.options.pageSizes[i] + '</option>');
            }

            //Select current page size
            $pageSizeChangeCombobox.val(self.options.pageSize);

            //Change page size on combobox change
            $pageSizeChangeCombobox.change(function() {
                self._changePageSize(parseInt($(this).val()));
            });
        },

        /* Creates go to page area.
        *************************************************************************/
        _createGotoPageInput: function() {
            var self = this;

            if (!self.options.gotoPageArea || self.options.gotoPageArea == 'none') {
                return;
            }

            //Add a span to contain goto page items
            this._$gotoPageArea = $('<span></span>')
                .addClass('jtable-goto-page')
                .appendTo(self._$bottomPanel.find('.jtable-left-area'));

            //Goto page label
            this._$gotoPageArea.append('<span>' + self.options.messages.gotoPageLabel + ': </span>');

            //Goto page input
            if (self.options.gotoPageArea == 'combobox') {

                self._$gotoPageInput = $('<select></select>')
                    .appendTo(this._$gotoPageArea)
                    .data('pageCount', 1)
                    .change(function() {
                        self._changePage(parseInt($(this).val()));
                    });
                self._$gotoPageInput.append('<option value="1">1</option>');

            } else { //textbox

                self._$gotoPageInput = $('<input type="text" maxlength="10" value="' + self._currentPageNo + '" />')
                    .appendTo(this._$gotoPageArea)
                    .keypress(function(event) {
                        if (event.which == 13) { //enter
                            event.preventDefault();
                            self._changePage(parseInt(self._$gotoPageInput.val()));
                        } else if (event.which == 43) { // +
                            event.preventDefault();
                            self._changePage(parseInt(self._$gotoPageInput.val()) + 1);
                        } else if (event.which == 45) { // -
                            event.preventDefault();
                            self._changePage(parseInt(self._$gotoPageInput.val()) - 1);
                        } else {
                            //Allow only digits
                            var isValid = (
                                (47 < event.keyCode && event.keyCode < 58 && event.shiftKey == false && event.altKey == false)
                                    || (event.keyCode == 8)
                                    || (event.keyCode == 9)
                            );

                            if (!isValid) {
                                event.preventDefault();
                            }
                        }
                    });

            }
        },

        /* Refreshes the 'go to page' input.
        *************************************************************************/
        _refreshGotoPageInput: function() {
            if (!this.options.gotoPageArea || this.options.gotoPageArea == 'none') {
                return;
            }

            if (this._totalRecordCount <= 0) {
                this._$gotoPageArea.hide();
            } else {
                this._$gotoPageArea.show();
            }

            if (this.options.gotoPageArea == 'combobox') {
                var oldPageCount = this._$gotoPageInput.data('pageCount');
                var currentPageCount = this._calculatePageCount();
                if (oldPageCount != currentPageCount) {
                    this._$gotoPageInput.empty();

                    //Skip some pages is there are too many pages
                    var pageStep = 1;
                    if (currentPageCount > 10000) {
                        pageStep = 100;
                    } else if (currentPageCount > 5000) {
                        pageStep = 10;
                    } else if (currentPageCount > 2000) {
                        pageStep = 5;
                    } else if (currentPageCount > 1000) {
                        pageStep = 2;
                    }

                    for (var i = pageStep; i <= currentPageCount; i += pageStep) {
                        this._$gotoPageInput.append('<option value="' + i + '">' + i + '</option>');
                    }

______________________________________________________

 

* End Part 3 *

______________________________________________________

Link to comment
Share on other sites

Replace ./admin/includes/modules/order_handler/js/01_02_jquery.jtable.js With This: (3/3 Part of this file)


                    this._$gotoPageInput.data('pageCount', currentPageCount);
                }
            }

            //same for 'textbox' and 'combobox'
            this._$gotoPageInput.val(this._currentPageNo);
        },

        /************************************************************************
        * OVERRIDED METHODS                                                     *
        *************************************************************************/

        /* Overrides load method to set current page to 1.
        *************************************************************************/
        load: function() {
            this._currentPageNo = 1;

            base.load.apply(this, arguments);
        },

        /* Used to change options dynamically after initialization.
        *************************************************************************/
        _setOption: function(key, value) {
            base._setOption.apply(this, arguments);

            if (key == 'pageSize') {
                this._changePageSize(parseInt(value));
            }
        },

        /* Changes current page size with given value.
        *************************************************************************/
        _changePageSize: function(pageSize) {
            if (pageSize == this.options.pageSize) {
                return;
            }

            this.options.pageSize = pageSize;

            //Normalize current page
            var pageCount = this._calculatePageCount();
            if (this._currentPageNo > pageCount) {
                this._currentPageNo = pageCount;
            }
            if (this._currentPageNo <= 0) {
                this._currentPageNo = 1;
            }

            //if user sets one of the options on the combobox, then select it.
            var $pageSizeChangeCombobox = this._$bottomPanel.find('.jtable-page-size-change select');
            if ($pageSizeChangeCombobox.length > 0) {
                if (parseInt($pageSizeChangeCombobox.val()) != pageSize) {
                    var selectedOption = $pageSizeChangeCombobox.find('option[value=' + pageSize + ']');
                    if (selectedOption.length > 0) {
                        $pageSizeChangeCombobox.val(pageSize);
                    }
                }
            }

            this._savePagingSettings();
            this._reloadTable();
        },

        /* Saves user preferences for paging
        *************************************************************************/
        _savePagingSettings: function() {
            if (!this.options.saveUserPreferences) {
                return;
            }

            this._setCookie('page-size', this.options.pageSize);
        },

        /* Overrides _createRecordLoadUrl method to add paging info to URL.
        *************************************************************************/
        _createRecordLoadUrl: function() {
            var loadUrl = base._createRecordLoadUrl.apply(this, arguments);
            loadUrl = this._addPagingInfoToUrl(loadUrl, this._currentPageNo);
            return loadUrl;
        },

        /* Overrides _createJtParamsForLoading method to add paging parameters to jtParams object.
        *************************************************************************/
        _createJtParamsForLoading: function () {
            var jtParams = base._createJtParamsForLoading.apply(this, arguments);
            
            if (this.options.paging) {
                jtParams.jtStartIndex = (this._currentPageNo - 1) * this.options.pageSize;
                jtParams.jtPageSize = this.options.pageSize;
            }

           var params = this._loadPostDataSettings();
           for(var name in params) {
               jtParams[name] = params[name];
           }

            return jtParams;
        },

        /* Overrides _addRowToTable method to re-load table when a new row is created.
        * NOTE: THIS METHOD IS DEPRECATED AND WILL BE REMOVED FROM FEATURE RELEASES.
        * USE _addRow METHOD.
        *************************************************************************/
        _addRowToTable: function ($tableRow, index, isNewRow) {
            if (isNewRow && this.options.paging) {
                this._reloadTable();
                return;
            }

            base._addRowToTable.apply(this, arguments);
        },

        /* Overrides _addRow method to re-load table when a new row is created.
        *************************************************************************/
        _addRow: function ($row, options) {
            if (options && options.isNewRow && this.options.paging && options.clientOnly != true) {
                this._reloadTable();
                return;
            }

            base._addRow.apply(this, arguments);
        },

        /* Overrides _removeRowsFromTable method to re-load table when a row is removed from table.
        *************************************************************************/
        _removeRowsFromTable: function ($rows, reason, clientOnly) {
            base._removeRowsFromTable.apply(this, arguments);

            if (this.options.paging) {
                if (this._$tableRows.length <= 0 && this._currentPageNo > 1) {
                    --this._currentPageNo;
                }

                if  (clientOnly != true)
                    this._reloadTable();
            }
        },

        /* Overrides _onRecordsLoaded method to to do paging specific tasks.
        *************************************************************************/
        _onRecordsLoaded: function (data) {
            if (this.options.paging) {
                this._totalRecordCount = data.TotalRecordCount;
                this._createPagingList();
                this._createPagingInfo();
                this._refreshGotoPageInput();
            }

            base._onRecordsLoaded.apply(this, arguments);
        },

        /************************************************************************
        * PRIVATE METHODS                                                       *
        *************************************************************************/

        /* Adds jtStartIndex and jtPageSize parameters to a URL as query string.
        *************************************************************************/
        _addPagingInfoToUrl: function (url, pageNumber) {
            if (!this.options.paging) {
                return url;
            }

            var jtStartIndex = (pageNumber - 1) * this.options.pageSize;
            var jtPageSize = this.options.pageSize;

            return (url + (url.indexOf('?') < 0 ? '?' : '&') + 'jtStartIndex=' + jtStartIndex + '&jtPageSize=' + jtPageSize);
        },

        /* Creates and shows the page list.
        *************************************************************************/
        _createPagingList: function () {
            if (this.options.pageSize <= 0) {
                return;
            }

            this._$pagingListArea.empty();
            if (this._totalRecordCount <= 0) {
                return;
            }

            var pageCount = this._calculatePageCount();

            this._createFirstAndPreviousPageButtons();
            if (this.options.pageList == 'normal') {
                this._createPageNumberButtons(this._calculatePageNumbers(pageCount));
            }
            this._createLastAndNextPageButtons(pageCount);
            this._bindClickEventsToPageNumberButtons();
        },

        /* Creates and shows previous and first page links.
        *************************************************************************/
        _createFirstAndPreviousPageButtons: function () {
            var $first = $('<span></span>')
                .addClass('jtable-page-number-first')
                .html('&lt&lt')
                .data('pageNumber', 1)
                .appendTo(this._$pagingListArea);

            var $previous = $('<span></span>')
                .addClass('jtable-page-number-previous')
                .html('&lt')
                .data('pageNumber', this._currentPageNo - 1)
                .appendTo(this._$pagingListArea);

            this._jqueryuiThemeAddClass($first, 'ui-button ui-state-default', 'ui-state-hover');
            this._jqueryuiThemeAddClass($previous, 'ui-button ui-state-default', 'ui-state-hover');

            if (this._currentPageNo <= 1) {
                $first.addClass('jtable-page-number-disabled');
                $previous.addClass('jtable-page-number-disabled');
                this._jqueryuiThemeAddClass($first, 'ui-state-disabled');
                this._jqueryuiThemeAddClass($previous, 'ui-state-disabled');
            }
        },

        /* Creates and shows next and last page links.
        *************************************************************************/
        _createLastAndNextPageButtons: function (pageCount) {
            var $next = $('<span></span>')
                .addClass('jtable-page-number-next')
                .html('&gt')
                .data('pageNumber', this._currentPageNo + 1)
                .appendTo(this._$pagingListArea);
            var $last = $('<span></span>')
                .addClass('jtable-page-number-last')
                .html('&gt&gt')
                .data('pageNumber', pageCount)
                .appendTo(this._$pagingListArea);

            this._jqueryuiThemeAddClass($next, 'ui-button ui-state-default', 'ui-state-hover');
            this._jqueryuiThemeAddClass($last, 'ui-button ui-state-default', 'ui-state-hover');

            if (this._currentPageNo >= pageCount) {
                $next.addClass('jtable-page-number-disabled');
                $last.addClass('jtable-page-number-disabled');
                this._jqueryuiThemeAddClass($next, 'ui-state-disabled');
                this._jqueryuiThemeAddClass($last, 'ui-state-disabled');
            }
        },

        /* Creates and shows page number links for given number array.
        *************************************************************************/
        _createPageNumberButtons: function (pageNumbers) {
            var previousNumber = 0;
            for (var i = 0; i < pageNumbers.length; i++) {
                //Create "..." between page numbers if needed
                if ((pageNumbers[i] - previousNumber) > 1) {
                    $('<span></span>')
                        .addClass('jtable-page-number-space')
                        .html('...')
                        .appendTo(this._$pagingListArea);
                }

                this._createPageNumberButton(pageNumbers[i]);
                previousNumber = pageNumbers[i];
            }
        },

        /* Creates a page number link and adds to paging area.
        *************************************************************************/
        _createPageNumberButton: function (pageNumber) {
            var $pageNumber = $('<span></span>')
                .addClass('jtable-page-number')
                .html(pageNumber)
                .data('pageNumber', pageNumber)
                .appendTo(this._$pagingListArea);

            this._jqueryuiThemeAddClass($pageNumber, 'ui-button ui-state-default', 'ui-state-hover');
            
            if (this._currentPageNo == pageNumber) {
                $pageNumber.addClass('jtable-page-number-active jtable-page-number-disabled');
                this._jqueryuiThemeAddClass($pageNumber, 'ui-state-active');
            }
        },

        /* Calculates total page count according to page size and total record count.
        *************************************************************************/
        _calculatePageCount: function () {
            var pageCount = Math.floor(this._totalRecordCount / this.options.pageSize);
            if (this._totalRecordCount % this.options.pageSize != 0) {
                ++pageCount;
            }

            return pageCount;
        },

        /* Calculates page numbers and returns an array of these numbers.
        *************************************************************************/
        _calculatePageNumbers: function (pageCount) {
            if (pageCount <= 4) {
                //Show all pages
                var pageNumbers = [];
                for (var i = 1; i <= pageCount; ++i) {
                    pageNumbers.push(i);
                }

                return pageNumbers;
            } else {
                //show first three, last three, current, previous and next page numbers
                var shownPageNumbers = [1, 2, pageCount - 1, pageCount];
                var previousPageNo = this._normalizeNumber(this._currentPageNo - 1, 1, pageCount, 1);
                var nextPageNo = this._normalizeNumber(this._currentPageNo + 1, 1, pageCount, 1);

                this._insertToArrayIfDoesNotExists(shownPageNumbers, previousPageNo);
                this._insertToArrayIfDoesNotExists(shownPageNumbers, this._currentPageNo);
                this._insertToArrayIfDoesNotExists(shownPageNumbers, nextPageNo);

                shownPageNumbers.sort(function (a, b) { return a - b; });
                return shownPageNumbers;
            }
        },

        /* Creates and shows paging informations.
        *************************************************************************/
        _createPagingInfo: function () {
            if (this._totalRecordCount <= 0) {
                this._$pageInfoSpan.empty();
                return;
            }

            var startNo = (this._currentPageNo - 1) * this.options.pageSize + 1;
            var endNo = this._currentPageNo * this.options.pageSize;
            endNo = this._normalizeNumber(endNo, startNo, this._totalRecordCount, 0);

            if (endNo >= startNo) {
                var pagingInfoMessage = this._formatString(this.options.messages.pagingInfo, startNo, endNo, this._totalRecordCount);
                this._$pageInfoSpan.html(pagingInfoMessage);
            }
        },

        /* Binds click events of all page links to change the page.
        *************************************************************************/
        _bindClickEventsToPageNumberButtons: function () {
            var self = this;
            self._$pagingListArea
                .find('.jtable-page-number,.jtable-page-number-previous,.jtable-page-number-next,.jtable-page-number-first,.jtable-page-number-last')
                .not('.jtable-page-number-disabled')
                .click(function (e) {
                    e.preventDefault();
                    self._changePage($(this).data('pageNumber'));
                });
        },

        /* Changes current page to given value.
        *************************************************************************/
        _changePage: function (pageNo) {
            pageNo = this._normalizeNumber(pageNo, 1, this._calculatePageCount(), 1);
            if (pageNo == this._currentPageNo) {
                this._refreshGotoPageInput();
                return;
            }

            this._currentPageNo = pageNo;
            this._reloadTable();
        }

    });

})(jQuery);


/************************************************************************
* SORTING extension for jTable                                          *
*************************************************************************/
(function ($) {

    //Reference to base object members
    var base = {
        _initializeFields: $.hik.jtable.prototype._initializeFields,
        _normalizeFieldOptions: $.hik.jtable.prototype._normalizeFieldOptions,
        _createHeaderCellForField: $.hik.jtable.prototype._createHeaderCellForField,
        _createRecordLoadUrl: $.hik.jtable.prototype._createRecordLoadUrl,
        _createJtParamsForLoading: $.hik.jtable.prototype._createJtParamsForLoading
    };

    //extension members
    $.extend(true, $.hik.jtable.prototype, {

        /************************************************************************
        * DEFAULT OPTIONS / EVENTS                                              *
        *************************************************************************/
        options: {
            sorting: false,
            multiSorting: false,
            defaultSorting: ''
        },

        /************************************************************************
        * PRIVATE FIELDS                                                        *
        *************************************************************************/

        _lastSorting: null, //Last sorting of the table

        /************************************************************************
        * OVERRIDED METHODS                                                     *
        *************************************************************************/

        /* Overrides base method to create sorting array.
        *************************************************************************/
        _initializeFields: function () {
            base._initializeFields.apply(this, arguments);

            this._lastSorting = [];
            if (this.options.sorting) {
                this._buildDefaultSortingArray();
            }
        },

        /* Overrides _normalizeFieldOptions method to normalize sorting option for fields.
        *************************************************************************/
        _normalizeFieldOptions: function (fieldName, props) {
            base._normalizeFieldOptions.apply(this, arguments);
            props.sorting = (props.sorting != false);
        },

        /* Overrides _createHeaderCellForField to make columns sortable.
        *************************************************************************/
        _createHeaderCellForField: function (fieldName, field) {
            var $headerCell = base._createHeaderCellForField.apply(this, arguments);
            if (this.options.sorting && field.sorting) {
                this._makeColumnSortable($headerCell, fieldName);
            }

            return $headerCell;
        },

        /* Overrides _createRecordLoadUrl to add sorting specific info to URL.
        *************************************************************************/
        _createRecordLoadUrl: function () {
            var loadUrl = base._createRecordLoadUrl.apply(this, arguments);
            loadUrl = this._addSortingInfoToUrl(loadUrl);
            return loadUrl;
        },

        /************************************************************************
        * PRIVATE METHODS                                                       *
        *************************************************************************/

        /* Builds the sorting array according to defaultSorting string
        *************************************************************************/
        _buildDefaultSortingArray: function () {
            var self = this;

            $.each(self.options.defaultSorting.split(","), function (orderIndex, orderValue) {
                $.each(self.options.fields, function (fieldName, fieldProps) {
                    if (fieldProps.sorting) {
                        var colOffset = orderValue.indexOf(fieldName);
                        if (colOffset > -1) {
                            if (orderValue.toUpperCase().indexOf(' DESC', colOffset) > -1) {
                                self._lastSorting.push({
                                    fieldName: fieldName,
                                    sortOrder: 'DESC'
                                });
                            } else {
                                self._lastSorting.push({
                                    fieldName: fieldName,
                                    sortOrder: 'ASC'
                                });
                            }
                        }
                    }
                });
            });
        },

        /* Makes a column sortable.
        *************************************************************************/
        _makeColumnSortable: function ($columnHeader, fieldName) {
            var self = this;
            
            $columnHeader
                .addClass('jtable-column-header-sortable')
                .click(function (e) {
                    e.preventDefault();

                    if (!self.options.multiSorting || !e.ctrlKey) {
                        self._lastSorting = []; //clear previous sorting
                    }
                    
                    self._sortTableByColumn($columnHeader);
                });

            //Set default sorting
            $.each(this._lastSorting, function (sortIndex, sortField) {
                if (sortField.fieldName == fieldName) {
                    if (sortField.sortOrder == 'DESC') {
                        $columnHeader.addClass('jtable-column-header-sorted-desc');
                    } else {
                        $columnHeader.addClass('jtable-column-header-sorted-asc');
                    }
                }
            });
        },

        /* Sorts table according to a column header.
        *************************************************************************/
        _sortTableByColumn: function ($columnHeader) {
            //Remove sorting styles from all columns except this one
            if (this._lastSorting.length == 0) {
                $columnHeader.siblings().removeClass('jtable-column-header-sorted-asc jtable-column-header-sorted-desc');
            }

            //If current sorting list includes this column, remove it from the list
            for (var i = 0; i < this._lastSorting.length; i++) {
                if (this._lastSorting[i].fieldName == $columnHeader.data('fieldName')) {
                    this._lastSorting.splice(i--, 1);
                }
            }

            //Sort ASC or DESC according to current sorting state
            if ($columnHeader.hasClass('jtable-column-header-sorted-asc')) {
                $columnHeader.removeClass('jtable-column-header-sorted-asc').addClass('jtable-column-header-sorted-desc');
                this._lastSorting.push({
                    'fieldName': $columnHeader.data('fieldName'),
                    sortOrder: 'DESC'
                });
            } else {
                $columnHeader.removeClass('jtable-column-header-sorted-desc').addClass('jtable-column-header-sorted-asc');
                this._lastSorting.push({
                    'fieldName': $columnHeader.data('fieldName'),
                    sortOrder: 'ASC'
                });
            }

            //Load current page again
            this._reloadTable();
        },

        /* Adds jtSorting parameter to a URL as query string.
        *************************************************************************/
        _addSortingInfoToUrl: function (url) {
            if (!this.options.sorting || this._lastSorting.length == 0) {
                return url;
            }

            var sorting = [];
            $.each(this._lastSorting, function (idx, value) {
                sorting.push(value.fieldName + ' ' + value.sortOrder);
            });

            return (url + (url.indexOf('?') < 0 ? '?' : '&') + 'jtSorting=' + sorting.join(","));
        },

        /* Overrides _createJtParamsForLoading method to add sorging parameters to jtParams object.
        *************************************************************************/
        _createJtParamsForLoading: function () {
            var jtParams = base._createJtParamsForLoading.apply(this, arguments);

            if (this.options.sorting && this._lastSorting.length) {
                var sorting = [];
                $.each(this._lastSorting, function (idx, value) {
                    sorting.push(value.fieldName + ' ' + value.sortOrder);
                });

                jtParams.jtSorting = sorting.join(",");
            }

            var params = this._loadPostDataSettings();
            for(var name in params) {
                jtParams[name] = params[name];
            }

            return jtParams;
        }

    });

})(jQuery);

/************************************************************************
* DYNAMIC COLUMNS extension for jTable                                  *
* (Show/hide/resize columns)                                            *
*************************************************************************/
(function ($) {

    //Reference to base object members
    var base = {
        _create: $.hik.jtable.prototype._create,
        _normalizeFieldOptions: $.hik.jtable.prototype._normalizeFieldOptions,
        _createHeaderCellForField: $.hik.jtable.prototype._createHeaderCellForField,
        _createCellForRecordField: $.hik.jtable.prototype._createCellForRecordField
    };

    //extension members
    $.extend(true, $.hik.jtable.prototype, {

        /************************************************************************
        * DEFAULT OPTIONS / EVENTS                                              *
        *************************************************************************/

        options: {
            tableId: undefined,
            columnResizable: true,
            columnSelectable: true
        },

        /************************************************************************
        * PRIVATE FIELDS                                                        *
        *************************************************************************/

        _$columnSelectionDiv: null,
        _$columnResizeBar: null,
        _cookieKeyPrefix: null,
        _currentResizeArgs: null,

        /************************************************************************
        * OVERRIDED METHODS                                                     *
        *************************************************************************/

        /* Overrides _addRowToTableHead method.
        *************************************************************************/

        _create: function () {
            base._create.apply(this, arguments);

            this._createColumnResizeBar();
            this._createColumnSelection();

            if (this.options.saveUserPreferences) {
                this._loadColumnSettings();
            }

            this._normalizeColumnWidths();
        },

        /* Normalizes some options for a field (sets default values).
        *************************************************************************/
        _normalizeFieldOptions: function (fieldName, props) {
            base._normalizeFieldOptions.apply(this, arguments);

            //columnResizable
            if (this.options.columnResizable) {
                props.columnResizable = (props.columnResizable != false);
            }

            //visibility
            if (!props.visibility) {
                props.visibility = 'visible';
            }
        },

        /* Overrides _createHeaderCellForField to make columns dynamic.
        *************************************************************************/
        _createHeaderCellForField: function (fieldName, field) {
            var $headerCell = base._createHeaderCellForField.apply(this, arguments);

            //Make data columns resizable except the last one
            if (this.options.columnResizable && field.columnResizable && (fieldName != this._columnList[this._columnList.length - 1])) {
                this._makeColumnResizable($headerCell);
            }

            //Hide column if needed
            if (field.visibility == 'hidden') {
                $headerCell.hide();
            }

            return $headerCell;
        },

        /* Overrides _createHeaderCellForField to decide show or hide a column.
        *************************************************************************/
        _createCellForRecordField: function (record, fieldName) {
            var $column = base._createCellForRecordField.apply(this, arguments);

            var field = this.options.fields[fieldName];
            if (field.visibility == 'hidden') {
                $column.hide();
            }

            return $column;
        },

        /************************************************************************
        * PUBLIC METHODS                                                        *
        *************************************************************************/

        /* Changes visibility of a column.
        *************************************************************************/
        changeColumnVisibility: function (columnName, visibility) {
            this._changeColumnVisibilityInternal(columnName, visibility);
            this._normalizeColumnWidths();
            if (this.options.saveUserPreferences) {
                this._saveColumnSettings();
            }
        },

        /************************************************************************
        * PRIVATE METHODS                                                       *
        *************************************************************************/

        /* Changes visibility of a column.
        *************************************************************************/
        _changeColumnVisibilityInternal: function (columnName, visibility) {
            //Check if there is a column with given name
            var columnIndex = this._columnList.indexOf(columnName);
            if (columnIndex < 0) {
                this._logWarn('Column "' + columnName + '" does not exist in fields!');
                return;
            }

            //Check if visibility value is valid
            if (['visible', 'hidden', 'fixed'].indexOf(visibility) < 0) {
                this._logWarn('Visibility value is not valid: "' + visibility + '"! Options are: visible, hidden, fixed.');
                return;
            }

            //Get the field
            var field = this.options.fields[columnName];
            if (field.visibility == visibility) {
                return; //No action if new value is same as old one.
            }

            //Hide or show the column if needed
            var columnIndexInTable = this._firstDataColumnOffset + columnIndex + 1;
            if (field.visibility != 'hidden' && visibility == 'hidden') {
                this._$table
                    .find('>thead >tr >th:nth-child(' + columnIndexInTable + '),>tbody >tr >td:nth-child(' + columnIndexInTable + ')')
                    .hide();
            } else if (field.visibility == 'hidden' && visibility != 'hidden') {
                this._$table
                    .find('>thead >tr >th:nth-child(' + columnIndexInTable + '),>tbody >tr >td:nth-child(' + columnIndexInTable + ')')
                    .show()
                    .css('display', 'table-cell');
            }

            field.visibility = visibility;
        },

        /* Prepares dialog to change settings.
        *************************************************************************/
        _createColumnSelection: function () {
            var self = this;

            //Create a div for dialog and add to container element
            this._$columnSelectionDiv = $('<div />')
                .addClass('jtable-column-selection-container')
                //.appendTo(self._$mainContainer);
                .insertAfter('#contentText');
            
            this._$table.children('thead').bind('contextmenu', function (e) {
                if (!self.options.columnSelectable) {
                    return;
                }
                
                e.preventDefault();

                //Make an overlay div to disable page clicks
                $('<div />')
                    .addClass('jtable-contextmenu-overlay')
                    .click(function () {
                        $(this).remove();
                        self._$columnSelectionDiv.hide();
                    })
                    .bind('contextmenu', function () { return false; })
                    //.appendTo(document.body);
                    .insertAfter('#contentText');

                self._fillColumnSelection();
                
                //Calculate position of column selection list and show it

                var containerOffset = self._$mainContainer.offset();
                var selectionDivTop = e.pageY - containerOffset.top;
                var selectionDivLeft = e.pageX - containerOffset.left;
                var containerHeight = self._$columnSelectionDiv.height() - 70;
                
                var selectionDivMinWidth = 100; //in pixels
                var containerWidth = self._$mainContainer.width();

                //If user clicks right area of header of the table, show list at a little left
                if ((containerWidth > selectionDivMinWidth) && (selectionDivLeft > (containerWidth - selectionDivMinWidth))) {
                    selectionDivLeft = containerWidth - selectionDivMinWidth;
                }

                self._$columnSelectionDiv.css({
                    left: selectionDivLeft,
                    top: containerOffset.top, //selectionDivTop,
                    'min-width': selectionDivMinWidth + 'px',
                    height: containerHeight + 'px',
                }).css( 'overflow-y', 'scroll' ).show();
            });
        },
        
        /* Prepares content of settings dialog.
        *************************************************************************/
        _fillColumnSelection: function () {
            var self = this;

            var $columnsUl = $('<ul></ul>')
                .addClass('jtable-column-select-list');
            for (var i = 0; i < this._columnList.length; i++) {
                var columnName = this._columnList[i];
                var field = this.options.fields[columnName];

                //Crete li element
                var $columnLi = $('<li></li>').appendTo($columnsUl);

                //Create label for the checkbox
                var $label = $('<label for="' + columnName + '"></label>')
                    .append($('<span>' + (field.title || columnName) + '</span>'))
                    .appendTo($columnLi);

                //Create checkbox
                var $checkbox = $('<input type="checkbox" name="' + columnName + '">')
                    .prependTo($label)
                    .click(function () {
                        var $clickedCheckbox = $(this);
                        var clickedColumnName = $clickedCheckbox.attr('name');
                        var clickedField = self.options.fields[clickedColumnName];
                        if (clickedField.visibility == 'fixed') {
                            return;
                        }

                        self.changeColumnVisibility(clickedColumnName, $clickedCheckbox.is(':checked') ? 'visible' : 'hidden');
                    });

                //Check, if column if shown
                if (field.visibility != 'hidden') {
                    $checkbox.attr('checked', 'checked');
                }

                //Disable, if column is fixed
                if (field.visibility == 'fixed') {
                    $checkbox.attr('disabled', 'disabled');
                }
            }

            this._$columnSelectionDiv.html($columnsUl);
        },

        /* creates a vertical bar that is shown while resizing columns.
        *************************************************************************/
        _createColumnResizeBar: function () {
            this._$columnResizeBar = $('<div />')
                .addClass('jtable-column-resize-bar')
                .appendTo(this._$mainContainer)
                .hide();
        },

        /* Makes a column sortable.
        *************************************************************************/
        _makeColumnResizable: function ($columnHeader) {
            var self = this;

            //Create a handler to handle mouse click event
            $('<div />')
                .addClass('jtable-column-resize-handler')
                .appendTo($columnHeader.find('.jtable-column-header-container')) //Append the handler to the column
                .mousedown(function (downevent) { //handle mousedown event for the handler
                    downevent.preventDefault();
                    downevent.stopPropagation();

                    var mainContainerOffset = self._$mainContainer.offset();

                    //Get a reference to the next column
                    var $nextColumnHeader = $columnHeader.nextAll('th.jtable-column-header:visible:first');
                    if (!$nextColumnHeader.length) {
                        return;
                    }

                    //Store some information to be used on resizing
                    var minimumColumnWidth = 10; //A column's width can not be smaller than 10 pixel.
                    self._currentResizeArgs = {
                        currentColumnStartWidth: $columnHeader.outerWidth(),
                        minWidth: minimumColumnWidth,
                        maxWidth: $columnHeader.outerWidth() + $nextColumnHeader.outerWidth() - minimumColumnWidth,
                        mouseStartX: downevent.pageX,
                        minResizeX: function () { return this.mouseStartX - (this.currentColumnStartWidth - this.minWidth); },
                        maxResizeX: function () { return this.mouseStartX + (this.maxWidth - this.currentColumnStartWidth); }
                    };

                    //Handle mouse move event to move resizing bar
                    var resizeonmousemove = function (moveevent) {
                        if (!self._currentResizeArgs) {
                            return;
                        }

                        var resizeBarX = self._normalizeNumber(moveevent.pageX, self._currentResizeArgs.minResizeX(), self._currentResizeArgs.maxResizeX());
                        self._$columnResizeBar.css('left', (resizeBarX - mainContainerOffset.left) + 'px');
                    };

                    //Handle mouse up event to finish resizing of the column
                    var resizeonmouseup = function (upevent) {
                        if (!self._currentResizeArgs) {
                            return;
                        }

                        $(document).unbind('mousemove', resizeonmousemove);
                        $(document).unbind('mouseup', resizeonmouseup);

                        self._$columnResizeBar.hide();

                        //Calculate new widths in pixels
                        var mouseChangeX = upevent.pageX - self._currentResizeArgs.mouseStartX;
                        var currentColumnFinalWidth = self._normalizeNumber(self._currentResizeArgs.currentColumnStartWidth + mouseChangeX, self._currentResizeArgs.minWidth, self._currentResizeArgs.maxWidth);
                        var nextColumnFinalWidth = $nextColumnHeader.outerWidth() + (self._currentResizeArgs.currentColumnStartWidth - currentColumnFinalWidth);

                        //Calculate widths as percent
                        var pixelToPercentRatio = $columnHeader.data('width-in-percent') / self._currentResizeArgs.currentColumnStartWidth;
                        $columnHeader.data('width-in-percent', currentColumnFinalWidth * pixelToPercentRatio);
                        $nextColumnHeader.data('width-in-percent', nextColumnFinalWidth * pixelToPercentRatio);

                        //Set new widths to columns (resize!)
                        $columnHeader.css('width', $columnHeader.data('width-in-percent') + '%');
                        $nextColumnHeader.css('width', $nextColumnHeader.data('width-in-percent') + '%');

                        //Normalize all column widths
                        self._normalizeColumnWidths();

                        //Finish resizing
                        self._currentResizeArgs = null;

                        //Save current preferences
                        if (self.options.saveUserPreferences) {
                            self._saveColumnSettings();
                        }
                    };

                    //Show vertical resize bar
                    self._$columnResizeBar
                        .show()
                        .css({
                            top: ($columnHeader.offset().top - mainContainerOffset.top) + 'px',
                            left: (downevent.pageX - mainContainerOffset.left) + 'px',
                            height: (self._$table.outerHeight()) + 'px'
                        });

                    //Bind events
                    $(document).bind('mousemove', resizeonmousemove);
                    $(document).bind('mouseup', resizeonmouseup);
                });
        },

        /* Normalizes column widths as percent for current view.
        *************************************************************************/
        _normalizeColumnWidths: function () {

            //Set command column width
            var commandColumnHeaders = this._$table
                .find('>thead th.jtable-command-column-header')
                .data('width-in-percent', 1)
                .css('width', '1%');

            //Find data columns
            var headerCells = this._$table.find('>thead th.jtable-column-header');

            //Calculate total width of data columns
            var totalWidthInPixel = 0;
            headerCells.each(function () {
                var $cell = $(this);
                if ($cell.is(':visible')) {
                    totalWidthInPixel += $cell.outerWidth();
                }
            });

            //Calculate width of each column
            var columnWidhts = {};
            var availableWidthInPercent = 100.0 - commandColumnHeaders.length;
            headerCells.each(function () {
                var $cell = $(this);
                if ($cell.is(':visible')) {
                    var fieldName = $cell.data('fieldName');
                    var widthInPercent = $cell.outerWidth() * availableWidthInPercent / totalWidthInPixel;
                    columnWidhts[fieldName] = widthInPercent;
                }
            });

            //Set width of each column
            headerCells.each(function () {
                var $cell = $(this);
                if ($cell.is(':visible')) {
                    var fieldName = $cell.data('fieldName');
                    $cell.data('width-in-percent', columnWidhts[fieldName]).css('width', columnWidhts[fieldName] + '%');
                }
            });
        },

        /* Saves field setting to cookie.
        *  Saved setting will be a string like that:
        * fieldName1=visible;23|fieldName2=hidden;17|...
        *************************************************************************/
        _saveColumnSettings: function () {
            var self = this;
            var fieldSettings = '';
            this._$table.find('>thead >tr >th.jtable-column-header').each(function () {
                var $cell = $(this);
                var fieldName = $cell.data('fieldName');
                var columnWidth = $cell.data('width-in-percent');
                var fieldVisibility = self.options.fields[fieldName].visibility;
                var fieldSetting = fieldName + "=" + fieldVisibility + ';' + columnWidth;
                fieldSettings = fieldSettings + fieldSetting + '|';
            });

            this._setCookie('column-settings', fieldSettings.substr(0, fieldSettings.length - 1));
        },

        /* Loads field settings from cookie that is saved by _saveFieldSettings method.
        *************************************************************************/
        _loadColumnSettings: function () {
            var self = this;
            var columnSettingsCookie = this._getCookie('column-settings');
            if (!columnSettingsCookie) {
                return;
            }

            var columnSettings = {};
            $.each(columnSettingsCookie.split('|'), function (inx, fieldSetting) {
                var splitted = fieldSetting.split('=');
                var fieldName = splitted[0];
                var settings = splitted[1].split(';');
                columnSettings[fieldName] = {
                    columnVisibility: settings[0],
                    columnWidth: settings[1]
                };
            });

            var headerCells = this._$table.find('>thead >tr >th.jtable-column-header');
            headerCells.each(function () {
                var $cell = $(this);
                var fieldName = $cell.data('fieldName');
                var field = self.options.fields[fieldName];
                if (columnSettings[fieldName]) {
                    if (field.visibility != 'fixed') {
                        self._changeColumnVisibilityInternal(fieldName, columnSettings[fieldName].columnVisibility);
                    }

                    $cell.data('width-in-percent', columnSettings[fieldName].columnWidth).css('width', columnSettings[fieldName].columnWidth + '%');
                }
            });
        }

    });

})(jQuery);


/************************************************************************
* MASTER/CHILD tables extension for jTable                              *
*************************************************************************/
(function ($) {

    //Reference to base object members
    var base = {
        _removeRowsFromTable: $.hik.jtable.prototype._removeRowsFromTable
    };

    //extension members
    $.extend(true, $.hik.jtable.prototype, {

        /************************************************************************
        * DEFAULT OPTIONS / EVENTS                                              *
        *************************************************************************/
        options: {
            openChildAsAccordion: false
        },

        /************************************************************************
        * PUBLIC METHODS                                                        *
        *************************************************************************/

        /* Creates and opens a new child table for given row.
        *************************************************************************/
        openChildTable: function ($row, tableOptions, opened) {
            var self = this;

            //Apply theming as same as parent table unless explicitily set
            if (tableOptions.jqueryuiTheme == undefined) {
                tableOptions.jqueryuiTheme = self.options.jqueryuiTheme;
            }

            //Show close button as default
            tableOptions.showCloseButton = (tableOptions.showCloseButton != false);

            //Close child table when close button is clicked (default behavior)
            if (tableOptions.showCloseButton && !tableOptions.closeRequested) {
                tableOptions.closeRequested = function () {
                    self.closeChildTable($row);
                };
            }

            //If accordion style, close open child table (if it does exists)
            if (self.options.openChildAsAccordion) {
                $row.siblings('.jtable-data-row').each(function () {
                    self.closeChildTable($(this));
                });
            }

            //Close child table for this row and open new one for child table
            self.closeChildTable($row, function () {
                var $childRowColumn = self.getChildRow($row).children('td').empty();
                var $childTableContainer = $('<div />')
                    .addClass('jtable-child-table-container')
                    .appendTo($childRowColumn);
                $childRowColumn.data('childTable', $childTableContainer);
                $childTableContainer.jtable(tableOptions);
                self.openChildRow($row);
                $childTableContainer.hide().slideDown('fast', function () {
                    if (opened) {
                        opened({
                             childTable: $childTableContainer
                        });
                    }
                });
            });
        },

        /* Closes child table for given row.
        *************************************************************************/
        closeChildTable: function ($row, closed) {
            var self = this;
            
            var $childRowColumn = this.getChildRow($row).children('td');
            var $childTable = $childRowColumn.data('childTable');
            if (!$childTable) {
                if (closed) {
                    closed();
                }

                return;
            }

            $childRowColumn.data('childTable', null);
            $childTable.slideUp('fast', function () {
                $childTable.jtable('destroy');
                $childTable.remove();
                self.closeChildRow($row);
                if (closed) {
                    closed();
                }
            });
        },

        /* Returns a boolean value indicates that if a child row is open for given row.
        *************************************************************************/
        isChildRowOpen: function ($row) {
            return (this.getChildRow($row).is(':visible'));
        },

        /* Gets child row for given row, opens it if it's closed (Creates if needed).
        *************************************************************************/
        getChildRow: function ($row) {
            return $row.data('childRow') || this._createChildRow($row);
        },

        /* Creates and opens child row for given row.
        *************************************************************************/
        openChildRow: function ($row) {
            var $childRow = this.getChildRow($row);
            if (!$childRow.is(':visible')) {
                $childRow.show();
            }

            return $childRow;
        },

        /* Closes child row if it's open.
        *************************************************************************/
        closeChildRow: function ($row) {
            var $childRow = this.getChildRow($row);
            if ($childRow.is(':visible')) {
                $childRow.hide();
            }
        },

        /************************************************************************
        * OVERRIDED METHODS                                                     *
        *************************************************************************/

        /* Overrides _removeRowsFromTable method to remove child rows of deleted rows.
        *************************************************************************/
        _removeRowsFromTable: function ($rows, reason) {
            //var self = this;

            if (reason == 'deleted') {
                $rows.each(function () {
                    var $row = $(this);
                    var $childRow = $row.data('childRow');
                    if ($childRow) {
                        //self.closeChildTable($row); //Removed since it causes "Uncaught Error: cannot call methods on jtable prior to initialization; attempted to call method 'destroy'"
                        $childRow.remove();
                    }
                });
            }

            base._removeRowsFromTable.apply(this, arguments);
        },

        /************************************************************************
        * PRIVATE METHODS                                                       *
        *************************************************************************/

        /* Creates a child row for a row, hides and returns it.
        *************************************************************************/
        _createChildRow: function ($row) {
            var totalColumnCount = this._$table.find('thead th').length;
            var $childRow = $('<tr></tr>')
                .addClass('jtable-child-row')
                .append('<td colspan="' + totalColumnCount + '"></td>');
            $row.after($childRow);
            $row.data('childRow', $childRow);
            $childRow.hide();
            return $childRow;
        }

    });

})(jQuery);

______________________________________________________

 

* End Part 4 *

______________________________________________________

Link to comment
Share on other sites

Replace ./admin/includes/modules/order_handler/js/01_03_order_handler_rev3_module.js With This: (1/2 Part of this File)

/*
  Advanced Order Handler Rev3 for osCommerce 2.3.3
  Copyright (C) 2014  Jonas [email protected]

  This file is part of Advanced Order Handler Rev3.

  Advanced Order Handler Rev3 is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Advanced Order Handler Rev3 is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with Advanced Order Handler Rev3.  If not, see <http://www.gnu.org/licenses/>.
*/

// Define Global Variables
var self,
ajaxRequest,
last_order_number = 0,
edit_order        = false,
polling           = false,
jTableOptions = $.hik.jtable.prototype.options.messages,
xmlHttp,
loading = $( '#ajax_loader' ).hide(),
oHandler = ( function( undefined ) {

    /* Order Handler: Plugin Configurations
     *************************************************************************/
    config = {
        message: "Records Loaded: {0} out of {1}",
        progress: 0,
        total: 0,
        clearSelection: true,
        openChildAsAccordion: false,
        toggleGritter: true
    },

    init = function( parameters ) {
        var childRow = function() {
            var img = $( this ).off( "click", childRow ).find( "span.glyphicon-resize-full" );
            $( "#orderTable" ).jtable( "closeChildTable", $( this ) );
            $( img ).removeClass( "glyphicon-resize-full" ).addClass( "glyphicon-resize-small" );
        };
        $( '#orderTable' ).jtable( {
            title: 'Order Handler',
            paging: true,
            pageSize: 10,
            sorting: true,
            multiSorting: true,
            jqueryuiTheme: false,
            selectOnRowClick: false,
            selecting: true, //Enable selecting
            multiselect: true, //Allow multiple selecting
            selectingCheckboxes: true, //Show checkboxes on first column
            defaultSorting: 'o.orders_id DESC',
            openChildAsAccordion: oHandler.config.openChildAsAccordion,
            ajaxSettings: {
                type: 'POST',
                dataType: 'json',
                //global: false
            },
            deleteConfirmation: function( data ) {
                data.deleteConfirmMessage = "Order <strong>#" + data.record.orders_id + "</strong> from customer <strong>" + data.record.customers_name + "</strong> will be deleted. Are you sure?" +
                "<div><input name='restock' type='checkbox' id='restock'><label for='restock' style='float:right;margin-top: 1em;margin-bottom: 0;'>" + TEXT_INFO_RESTOCK_PRODUCT_QUANTITY + "</label></div><script>$( '#restock' ).button();</script>";
            },
            tableId: 'jTable',
            actions: {
                listAction: function( postData, jtParams ) {
                    if ( "undefined" === typeof postData )
                        postData = {
                            status: "1"
                        };
                    return $.Deferred( function( $dfd ) {
                        $.ajax( {
                            url: encodeURI( 'get_table.php?action=get_orders&jtStartIndex=' + jtParams.jtStartIndex + '&jtPageSize=' + jtParams.jtPageSize + '&jtSorting=' + jtParams.jtSorting ),
                            type: 'POST',
                            dataType: 'json',
                            data: postData,
                            beforeSend: function( xhr ) {
                                settings = {
                                    message: jTableOptions.loadingMessage,
                                };
                                if ( false === edit_order ) oHandler.showBusy.setup( settings );
                            },
                            //global: false,
                            success: function( data ) {
                                $dfd.resolve( data );
                            },
                            error: function() {
                                $dfd.reject();
                            }
                        } );
                    } );
                },
                createAction: 'get_table.php?action=create_new_order',
                updateAction: 'get_table.php?action=update_order_fields',
                deleteAction: function( postData ) {
                    var restock = $( "#restock" ).prop( "checked" );
                    postData.restock = restock;
                    return $.Deferred( function( $dfd ) {
                        $.ajax( {
                            url: 'get_table.php?action=delete_order',
                            type: 'POST',
                            //global: false,
                            dataType: 'json',
                            data: postData,
                            success: function( data ) {
                                $dfd.resolve( data );
                            },
                            error: function() {
                                $dfd.reject();
                            }
                        } )
                            .always( function() {
                                oHandler.showBusy.hide();
                            } );
                    } );
                }
            },
            toolbar: {
                items: [ {
                    text: 'Configuration',
                    iconCSS: 'fa-cogs',
                    cssClass: 'configurationModal',
                }, {
                    text: 'Calculate Orders Totals',
                    iconCSS: 'fa-dollar',
                    click: function() {
                        var orders_total = 0,
                        selectedRows = $( "#orderTable" ).jtable( "selectedRows" ),
                        numRows = selectedRows.length;

                        for ( var i = 0; i < numRows; i++ ) {
                            orders_total += parseFloat( selectedRows.find( "td.order_total" ).eq( i ).text()
                            .replace( /[^0-9\,\.]+/g, "" )
                            .replace( /(,|\.)(?=[^.]*(,|\.))/, "" )
                            .replace( /\D/g, "." )
                            .replace( /\.(?=[^.]*\.)/, "" ));
                        }
                        $.fn.tdialog( {
                            type: "info",
                            content: "Combined Order Totals for selected rows are " + orders_total + ".",
                            title: "Calculate Orders Totals",
                            icon: "info",
                            showOverlay: oHandler.configuration.settings.showOverlay,
                            effect: 'css3',
                            css3EffectIn: '',
                            css3EffectOut: 'bounce',
                        } );
                    }
                },
                {
                    text: 'Statistics',
                    iconCSS: 'fa-bar-chart-o',
                    cssClass: 'statistics',
                    } ]
                },
            fields: {
                edit_order: {
                    title: '',
                    width: '1%',
                    sorting: false,
                    edit: false,
                    create: false,
                    display: function( data ) {
                        return data.record.edit_order;
                    }
                },
                orders_id: {
                    title: ENTRY_ORDER_NUMBER,
                    listClass: 'center jtable-selecting-column select_me',
                    edit: false,
                    key: true,
                    create: false,
                    width: '9%',
                    display: function( data ) {
                        return '<strong>' + data.record.orders_id + '</strong>';
                    }
                },
                customers_name: {
                    title: ENTRY_CUSTOMER,
                    listClass: 'customers_name',
                    edit: true,
                    create: false,
                    width: '13%'
                },
                //CHILD TABLE DEFINITION FOR "ORDER"
                order: {
                    title: '',
                    width: '1%',
                    sorting: false,
                    edit: false,
                    create: false,
                    listClass: 'expand_order center ajax_disable jtable-command-column',
                    selecting: false,
                    selectOnRowClick: false,
                    display: function( data ) {
                        var img = $( data.record.order );
                        //Open child table when user clicks the image
                        $( img ).on( "click", function() {
                            var pos = $( "#orderTable" ).jtable( "isChildRowOpen", $( img ).closest( 'tr' ) ),
                                products = data.record.products;

                            this.className = "glyphicon glyphicon-resize-small tooltip_set";
                            $( this ).closest( "tr" ).off( "click", childRow );

                            if ( pos === true )
                                return $( "#orderTable" ).jtable( "closeChildTable", $( img ).closest( 'tr' ) );

                            $( "#orderTable" ).find( "span.glyphicon-resize-full" ).removeClass( "glyphicon-resize-full" ).addClass( "glyphicon-resize-small" );

                            this.className = "glyphicon glyphicon-resize-full tooltip_set";

                            $( "#orderTable" ).jtable( "openChildTable",
                                $( img ).closest( 'tr' ), {
                                    title: "Order #<span class='select_me'>" + data.record.orders_id + "</span>",
                                    selecting: false,
                                    selectOnRowClick: false,
                                    jqueryuiTheme: false,
                                    tableId: 'productTotals',
                                    actions: {
                                        listAction: 'get_table.php?action=get_order&oID=' + data.record.orders_id
                                    },
                                    fields: {
                                        orders_id: {
                                            key: true,
                                            type: 'hidden'
                                        },
                                        customer_address: {
                                            title: ENTRY_CUSTOMER,
                                            width: '20%'
                                        },
                                        shipping_address: {
                                            title: ENTRY_SHIPPING_ADDRESS,
                                            width: '10%'
                                        },
                                        billing_address: {
                                         title: ENTRY_BILLING_ADDRESS,
                                         width: '10%'
                                        },
                                        email_address: {
                                            title: ENTRY_EMAIL_ADDRESS,
                                            width: '20%'
                                        },
                                        telephone: {
                                            title: ENTRY_TELEPHONE_NUMBER,
                                            width: '10%'
                                        },
                                        shipping_method: {
                                            listClass: 'ajax_disable',
                                            title: ENTRY_SHIPPING_METHOD,
                                            width: '20%'
                                        },
                                        payment_method: {
                                            listClass: 'ajax_disable',
                                            title: ENTRY_PAYMENT_METHOD,
                                            width: '20%'
                                    },
                                    },
                                    recordsLoaded: function( event, data ) {
                                        var row = $( this ).find( "tr" ).eq( 1 ),
                                            productsRow = $( "#orderTable" ).jtable( "openChildRow", $( row ) ),
                                            parentRow = $( this ).closest( "tr" ).prev(),
                                            jtable_title = $( parentRow ).next().find( "div.jtable-title-text-next" );

                                        $( productsRow ).children( "td" )
                                            //.css({ opacity:0 })
                                            .css({ width:'100%' })
                                            .css({ position:'absolute' })
                                            .html( data.records[ 0 ].products ).find("#tdb-update").button({icons:{primary:"ui-icon-disk"}}).addClass("ui-priority-primary").parent().removeClass("tdbLink");
                                        $( productsRow ).closest( "table.jtable" )
                                            .find('#change_order_status, #shipping_method_pull_down, #payment_method_pull_down').multiselect({ buttonClass: 'btn btn-xs btn-info' });

                                            var h = $( productsRow ).find( "table" ).eq(0).height() + $( productsRow ).find( "table" ).eq(1).height() + 5;


                                        $( productsRow ).animate({ height:h + 'px' }, 400, function( event ) {
                                            $( productsRow ).children( "td" )
                                                .css({ position:'static' });
                                        });
                                        //$( productsRow ).children( "td" ).animate({ opacity: 1 }, 400);

                                        $( parentRow ).on( "click", childRow );
                                        $( jtable_title ).on( "click", function() {
                                            $( img ).removeClass( "glyphicon-resize-full" ).addClass( "glyphicon-resize-small" );
                                            var childRow = $( this ).closest( "tr.jtable-child-row" ).prev();
                                            $( "#orderTable" ).jtable( "closeChildTable", $( childRow ) );
                                            return $( this ).off();
                                        } );

                                        // Initiate Create new field Dialog
                                        //oHandler.dialogForm.dialog();
                                    },
                                    closeRequested: function( event, data ) {
                                        $( img ).removeClass( "glyphicon-resize-full" ).addClass( "glyphicon-resize-small" );
                                        $( "#orderTable" ).jtable( "closeChildTable", $( img ).closest( 'tr' ) );
                                    }
                                }, function( data ) { //opened handler
                                    data.childTable.jtable( 'load' );
                                } );

                        } );
                        // Return image to show on the order row
                        return img;
                    }
                },
                order_total: {
                    title: TABLE_HEADING_ORDER_TOTAL,
                    listClass: 'order_total',
                    edit: false,
                    create: false,
                    width: '10%'
                },
                date_purchased: {
                    title: TABLE_HEADING_DATE_PURCHASED,
                    listClass: 'date_purchased',
                    edit: false,
                    create: false,
                    width: '15%'
                },
                orders_status: {
                    title: TABLE_HEADING_STATUS,
                    listClass: 'ajax_disable',
                    edit: false,
                    create: false,
                    width: '13%',
                    display: function( data ) {
                        return data.record.orders_status;
                    }
                },
                orders_status_text: {
                    title: TABLE_HEADING_STATUS,
                    edit: false,
                    create: false,
                    width: '13%',
                    visibility: 'hidden',
                    display: function( data ) {
                        return data.record.orders_status_text;
                    }
                },
                shipping_method: {
                    title: ENTRY_SHIPPING_METHOD,
                    listClass: 'ajax_disable',
                    width: '8%',
                    edit: false,
                    create: false,
                    display: function( data ) {
                        return data.record.shipping_method;
                    }
                },
                shipping_method_text: {
                    title: ENTRY_SHIPPING_METHOD,
                    width: '8%',
                    edit: false,
                    create: false,
                    visibility: 'hidden',
                    display: function( data ) {
                        return data.record.shipping_method_text;
                    }
                },
                payment_method: {
                    title: ENTRY_PAYMENT_METHOD,
                    listClass: 'ajax_disable',
                    width: '8%',
                    edit: false,
                    create: false,
                    display: function( data ) {
                        return data.record.payment_method;
                    }
                },
                payment_method_text: {
                    title: ENTRY_PAYMENT_METHOD,
                    width: '16%',
                    edit: false,
                    create: false,
                    visibility: 'hidden',
                    display: function( data ) {
                        return data.record.payment_method_text;
                    }
                },
                products_quantity: {
                    title: TABLE_HEADING_QUANTITY,
                    listClass: 'products_purchased center',
                    edit: false,
                    create: false,
                    width: '4%'
                },
                comments: {
                    title: TABLE_HEADING_COMMENTS,
                    edit: false,
                    create: false,
                    width: '14%',
                    display: function( data ) {
                        return data.record.comments;
                    }
                },
                search_customer_orders: {
                    title: '',
                    width: '1%',
                    sorting: false,
                    edit: false,
                    create: false,
                    display: function( data ) {
                        return data.record.search_customer_orders;
                    }
                },
                mail_confirmation: {
                    title: '',
                    width: '1%',
                    sorting: false,
                    edit: false,
                    create: false,
                    display: function( data ) {
                        return data.record.mail_confirmation;
                    }
                },
                duplicate_order: {
                    title: '',
                    width: '1%',
                    sorting: false,
                    edit: false,
                    create: false,
                    display: function( data ) {
                        return data.record.duplicate_order;
                    }
                },
                mail_customer: {
                    title: '',
                    width: '1%',
                    sorting: false,
                    edit: false,
                    create: false,
                    display: function( data ) {
                        return data.record.mail_customer;
                    }
                },
                /* Here Create new Order Begins */
                customers_id: {
                    type: 'hidden',
                    edit: false,
                    create: true,
                    sorting: false,
                    display: function( data ) {
                        return data.records.customers_id;
                    }
                },
                search_customer: {
                    title: HEADING_TITLE_SEARCH_CUSTOMERS,
                    visibility: 'hidden',
                    create: true,
                    edit: false,
                    sorting: false
                },
                customers_firstname: {
                    title: ENTRY_FIRST_NAME,
                    visibility: 'hidden',
                    create: true,
                    sorting: false,
                    edit: false
                },
                customers_lastname: {
                    title: ENTRY_LAST_NAME,
                    visibility: 'hidden',
                    create: true,
                    sorting: false,
                    edit: false
                },
                delivery_company: {
                    title: ENTRY_COMPANY,
                    visibility: 'hidden',
                    create: true,
                    edit: true,
                    display: function( data ) {
                        return data.record.customers_company;
                    }
                },
                customers_email_address: {
                    title: ENTRY_EMAIL_ADDRESS,
                    visibility: 'hidden',
                    defultValue: "true",
                    create: true,
                    display: function( data ) {
                        return data.record.customers_email_address;
                    }
                },
                delivery_street_address: {
                    title: ENTRY_STREET_ADDRESS,
                    visibility: 'hidden',
                    create: true,
                    display: function( data ) {
                        return data.record.delivery_street_address;
                    }
                },
                delivery_postcode: {
                    title: ENTRY_POST_CODE,
                    visibility: 'hidden',
                    create: true,
                    display: function( data ) {
                        return data.record.delivery_postcode;
                    }
                },
                delivery_state: {
                    title: ENTRY_STATE,
                    visibility: 'hidden',
                    create: true,
                    display: function( data ) {
                        return data.record.delivery_state;
                    }
                },
                delivery_city: {
                    title: ENTRY_CITY,
                    visibility: 'hidden',
                    create: true,
                    display: function( data ) {
                        return data.record.delivery_city;
                    }
                },
                delivery_country: {
                    title: ENTRY_COUNTRY,
                    visibility: 'hidden',
                    options: country_list,
                    create: true,
                    display: function( data ) {
                        return data.record.delivery_country;
                    }
                },
                customers_telephone: {
                    title: ENTRY_TELEPHONE_NUMBER,
                    visibility: 'hidden',
                    create: true,
                    display: function( data ) {
                        return data.record.customers_telephone;
                    }
                },
                customers_fax: {
                    title: ENTRY_FAX_NUMBER,
                    visibility: 'hidden',
                    create: true,
                    edit: false
                },
                payment_method_code: {
                    title: ENTRY_PAYMENT_METHOD,
                    visibility: 'hidden',
                    options: payment_modules,
                    create: true
                },
                shipping_module: {
                    title: ENTRY_SHIPPING_METHOD,
                    visibility: 'hidden',
                    options: shipping_modules,
                    create: true
                },
                Currency: {
                    title: ENTRY_CURRENCY,
                    visibility: 'hidden',
                    options: currencies,
                    create: true,
                    edit: false
                }
            },
            loadingRecords: function( event, data ) {
                //console.log('loadingRecords event fired');
            },
            recordAdded: function( event, data ) {
                //console.log( "recordAdded" );
            },
            recordUpdated: function( event, data ) {
                //console.log( "recordUpdated" );
            },
            recordsLoaded: function( event, data ) {
                // Determine Last Order Number
                if ( 0 < data.records.length ) {
                    if ( last_order_number < data.records[ 0 ].orders_id )
                        last_order_number = data.records[ 0 ].orders_id;
                } else {
                    last_order_number = data.serverResponse.LastOrderNumber;
                }

                // Initiate Send Email Modal
                oHandler.contact.init();

                $( "select.orders_status" ).multiselect({ buttonClass: 'btn btn-xs btn-info' });

                $( "select.payment_method_pull_down" ).multiselect({ buttonClass: 'btn btn-xs btn-info' });

                $( "select.shipping_method_pull_down" ).multiselect({ buttonClass: 'btn btn-xs btn-info' });

                // Initiate Ajax Long Polling
                // oHandler.messagesLongPolling.init( event );
                var switchOn = ( true === edit_order ? false : true );
                if ( true === polling && undefined !== last_order_number )
                    oHandler.messagesLongPolling.toggle( event, switchOn );

            },
            rowInserted: function( event, data ) {
                $( data.row[ 0 ].firstChild.firstChild )
                    .attr( "name", "batch_order_numbers[" + data.record.orders_id + "]" ).val( "no" );

                $( data.row ).addClass( "contextMenu" );
            },
            formCreated: function( event, data ) {
                //console.log( "formCreated" );

                // Initiate Bootstrap Multiselect on <select>
                data.form.find( "select" )
                .multiselect({
                    buttonClass: 'btn btn-xs btn-info',
                    maxHeight: 200,
                    enableCaseInsensitiveFiltering: true,
                });

                // Find Option for Selected Payment Method
                if ( "edit" == data.formType ) {
                    var payment_method = $( "#payment_method_" + data.record.orders_id )[0].innerText,
                    selected           = data.form.find( "#Edit-payment_method_code" ).find( "option" ).filter(function () { return $(this).html() == payment_method; }).val();
                    $( "#Edit-payment_method_code" )
                        .multiselect('select', selected )
                        .multiselect('refresh');
                }

                if ( 0 < $( "#Edit-search_customer" ).length ) {
                    var cache = {};
                    $( "#Edit-search_customer" ).autocomplete( {
                        minLength: 2,
                        select: function( event, ui ) {
                            window.location = ( ui.item.id );
                        },
                        source: function( request, response ) {
                            var term = request.term;
                            if ( term in cache ) {
                                response( cache[ term ] );
                                return;
                            }

                            $.ajax( {
                                dataType: "json",
                                url: "get_table.php?action=search_customers",
                                data: request,
                                //global: false
                            } ).done( function( data, status, xhr ) {
                                cache[ term ] = data;
                                response( data );
                            } );
                        }
                    } ).data( "ui-autocomplete" )._renderItem = function( ul, item ) {
                        return $( "<li></li>" )
                            .data( "item.autocomplete", item )
                            .append( "<a href='" + item.id + "' class='search_customer'>" + item.value + "</a>" )
                            .click( function( e ) {
                                e.preventDefault();
                                e.stopPropagation();
                                href = $( this ).find( "a" ).attr( "href" );
                                $.ajax( {
                                    dataType: "json",
                                    url: encodeURI( href ),
                                    type: 'GET',
                                    //global: false
                                } )
                                .done( function( data, status, xhr ) {
                                    $( "#Edit-customers_id" ).val( data.customers_id );
                                    $( "#Edit-customers_firstname" ).val( data.customers_firstname );
                                    $( "#Edit-customers_lastname" ).val( data.customers_lastname );
                                    $( "#Edit-customers_email_address" ).val( data.customers_email_address );
                                    $( "#Edit-delivery_street_address" ).val( data.entry_street_address );
                                    $( "#Edit-delivery_postcode" ).val( data.entry_postcode );
                                    $( "#Edit-delivery_state" ).val( data.entry_state );
                                    $( "#Edit-delivery_city" ).val( data.entry_city );
                                    $( "#Edit-customers_telephone" ).val( data.customers_telephone );
                                    $( "#Edit-customers_fax" ).val( data.customers_fax );
                                    $( "#Edit-delivery_country" ).multiselect('select', $( "#Edit-delivery_country" ).find( "option" ).eq( ( data.entry_country_id - 2 ) )[0].value );
                                } )
                                .always( function() {
                                    $( "#Edit-search_customer" ).autocomplete( "close" );
                                });
                            } )
                            .appendTo( ul );
                    };
                }
            },
            selectionChanged: function( event, data ) {
                //console.log( "selectionChanged" );
            }
        } );

        // Load all records when page is first shown
        $( "#orderTable" ).jtable( "load", parameters );

    },


    /* Display jTable Message
    *************************************************************************/
    showBusy = {

        setup: function ( settings ) {
            $.extend( config, settings );
            $( "#orderTable" ).jtable( "showBusy", oHandler.showBusy.formatString( config.message, config.progress, config.total ), 0 );
        },

        update: function ( progress ) {
            $("#orderTable").jtable( "showBusy", oHandler.showBusy.formatString( config.message, progress, config.total ), 0 );
            if ( config.total == progress )
                oHandler.showBusy.hide();
        },

        hide: function () {
            $( "#orderTable" ).jtable( "hideBusy" );
        },

        formatString: function () {
            if ( 0 === arguments.length ) {
                return null;
            }

            var str = arguments[0],
            length  = arguments.length;
            for ( var i = 1; i < length; i++ ) {
                var placeHolder = "{" + (i - 1) + "}";
                str = str.replace( placeHolder, arguments[i] );
            }

            return str;
        }
    },

    /* Order Handler Configuration Modal
    *************************************************************************/
    configuration = {

        settings: {
            self: $( "#order_handler_config" ),
            configModal: $( "#configModal" ),
            PHPMailer: false,
            printField: $( "#print" ),
            showOverlay: true,
            debugTime: false,
            set: false,
            newWin: false,
            key_code: "",
        },

        init: function() {
            var s = this;

            $( "body" ).on( "click", "#orderTable span.configurationModal", function( event ) {
                //s.settings.self.find( "li" ).tooltip('toggle').tooltip('hide');
                s.settings.self.simpleModal({
                    closeHTML: "<a href='#' title='Close' class='modal-close'>x</a>",
                    close: true,
                    persist: true,
                    position: ["15%",],
                    overlayId: 'contact-overlay',
                    overlayClose: true,
                    containerId: 'config-container',
                    onOpen: oHandler.configuration.open,
                    //onShow: oHandler.configuration.show,
                    onClose: oHandler.configuration.close
                });
            });

            if ( false === oHandler.configuration.settings.set ) {
                oHandler.configuration.show(this);
                oHandler.configuration.settings.set = true;
            }

             $( "#orderTable" ).find( "span.statistics" ).on( "click", function( event ) {
                oHandler.statistics.init();
            });

        },

        open: function(dialog) {
            if ( "true" == localStorage.debugTime )
                $( "#configModal" ).find( "input" ).eq(6).iCheck('check');
            if ( true === oHandler.configuration.settings.newWin )
                $( "#configModal" ).find( "input" ).eq(7).iCheck('check');

            dialog.overlay.fadeIn('fast', function () {
                dialog.container.slideDown('fast', function () {
                    dialog.data.fadeIn('fast');
                });
            });
        },

        close: function(dialog) {
            $( "#config-container" ).hide( "drop", { direction: "down" }, "fast", function() {
                $.simpleModal.close(); // must call this!
            });
        },

        show: function( s ) {
            var configButton = $( "#configModal" ).find( "input.configButton, button.configButton" ),
            enableButton    = $( "#enable_button" ),
            disableButton   = $( "#disable_button" );

            // Initialize iCheck Radio/Checkbox fields for Configuration Modal
            configButton.iCheck({
                checkboxClass: 'icheckbox_flat-orange',
                radioClass: 'iradio_flat-orange'
            });

            // Tooltips
            if ( "true" == localStorage.toolTips ) {
                s.settings.configModal.find( 'li, button').qtip({
                    style: {
                        classes: 'qtip-bootstrap',
                    },
                    content: {
                        title: function( event, api ) {
                            if ( "button" == this[0].tagName.toLowerCase() )
                                return this.html();
                            return this.find( "label" ).html();
                        },
                        button: true
                    },
                    position: {
                        my: 'top left',
                        at: 'bottom left',
                        target: 'event',
                        viewport: $(window)
                    },
                    show: {
                        solo: true,
                        delay: 800,
                    },
                    hide: {
                        distance: 30
                    }
                });
            }

            configButton.eq(0).on( "ifToggled", function( event ) {
                if ( this.checked )
                    s.settings.printField.val( true );
                else
                    s.settings.printField.val( false );
            });

            configButton.eq(1).on( "ifToggled", function( event ) {
                if ( this.checked ) {
                    oHandler.showTooltips( true );
                    localStorage.toolTips = true;
                } else {
                    if ( "true" == localStorage.toolTips ) {
                        $( "#jTable" ).find( '.tooltip_set').qtip('destroy');
                        $( "#navigationBottom" ).find( '.tooltip_set').qtip('destroy');
                        $( "#navigationTop" ).find( '.tooltip_set').qtip('destroy');
                        $( "#configModal" ).find( 'li').qtip('destroy');
                        // Gritter Notification
                        oHandler.gritter( "Tooltips Disabled", "", "fa-wheelchair" );
                        localStorage.toolTips = false;
                    }
                }
            });

            configButton.eq(2).on( "ifToggled", function( event ) {
                if ( this.checked )
                    s.hideHeader( false );
                else
                    s.hideHeader( true );
            });

            configButton.eq(3).on( "ifToggled", function( event ) {
                if ( this.checked )
                    oHandler.config.clearSelection = true;
                else
                    oHandler.config.clearSelection = false;
            });

            configButton.eq(4).on( "ifToggled", function( event ) {
                if ( this.checked )
                    oHandler.config.toggleGritter = true;
                else
                    oHandler.config.toggleGritter = false;
            });

            configButton.eq(5).on( "ifToggled", function( event ) {
                if ( this.checked )
                    s.settings.showOverlay = true;
                else
                    s.settings.showOverlay = false;
            });

            configButton.eq(6).on( "ifToggled", function( event ) {
                if ( this.checked )
                    localStorage.debugTime = true;
                else
                    localStorage.debugTime = false;
            });

            configButton.eq(7).on( "ifToggled", function( event ) {
                if ( this.checked )
                    s.settings.newWin = true;
                else
                    s.settings.newWin = false;
            });

            configButton.eq(8).on( "ifToggled", function( event ) {
                if ( this.checked )
                    s.settings.PHPMailer = true;
                else
                    s.settings.PHPMailer = false;
            });

            configButton.eq(9).on( "click", function( event ) {
                s.bindKey( { shortcutName: "jTable Refresh", shortcut: "refreshKey", func: '$( "#orderTable" ).jtable( "reload" )' } );
            });

            configButton.eq(10).on( "click", function( event ) {
                var content = '<div class="content" style="min-height: 50px;"><div class="form-control" style="border: none;box-shadow: 0 0;">Enter Function:</div><input class="form-control" type="text" id="new-function"></div>';
                 $.fn.tdialog({
                     type:"confirm",
                     content:content,
                     icon:"confirm",
                     titleBar:false,
                     confirmCallback:function() {
                         var size = 0, key;
                         for (key in keyShortcuts) {
                             if (keyShortcuts.hasOwnProperty(key)) size++;
                         }
                         setTimeout( function(){ s.bindKey( { shortcutName: "jTable Custom Function", shortcut: $.trim( $( "#new-function" )[0].value ), func: $( "#new-function" )[0].value } ); },100);

                     }
                });
            });

            configButton.eq(11).on( "click", function( event ) {
                 $.fn.tdialog({
                     type:"confirm",
                     content:"Remove all Keyboard Shortcuts?",
                     icon:"confirm",
                     titleBar:false,
                     confirmCallback:function() {
                         for ( var n in keyShortcuts ) {
                             $( "body" ).off( "keyup." + keyShortcuts[ n ].key );
                         }
                         localStorage.removeItem('keyShortcuts');
                         keyShortcuts = {};
                     }
                });

            });

            // Ajax Long Polling Buttons
            enableButton.on( "click", function( event ) {
                oHandler.messagesLongPolling.toggle( event );
                oHandler.messagesLongPolling.start();
            });
            disableButton.on( "click", function( event ) {
                if ( false === polling )
                    return false;
                oHandler.messagesLongPolling.toggle( event );
            });

            $( "#context_menu" ).on( "ifToggled", function( event ) {
                $.saveCookie( $( this ) );
            });

            // Save any changes to Cookie
            configButton.on( "ifToggled", function( event ) {
                $.saveCookie( $( "#configModal" ) );
            });

        },

        /* Hide Header
        *************************************************************************/
        hideHeader: function( toggle ) {
            var message = "";
            if ( true === toggle )
                message      = "Hide Header";
            else
                message      = "Show Header";

            $( "#body_content" ).toggleClass( "toggleHeader" );
            $( "#adminAppMenu" ).toggleClass( "toggleHeader" );
            $( "#js-switch" ).click();

            // Gritter Notification
            oHandler.gritter( message, "", "fa-header" );
        },

        /* Bind Keyboard Shortcut
        *************************************************************************/
        bindKey: function( options  ) {
            var s   = this,
            content = '<div class="content" style="min-height: 50px;"><div class="form-control" style="border: none;box-shadow: 0 0;">Enter new Keyboard Shortcut for ' + options.shortcutName + ':</div><input class="form-control" type="text" id="newValue" maxlength="1"></div>',
            tdialog =
            $.fn.tdialog({
                type:"confirm",
                content:content,
                icon:"confirm",
                titleBar:false,
                confirmCallback:function() {
                    keyShortcuts[ options.shortcut ] = s.settings.key_code;
                    localStorage.keyShortcuts = JSON.stringify(keyShortcuts);
                    s.restoreKeys();
                    //$.fn.closeTdialog();
                    $( "#tdialog" ).remove();
                }
           });

            $.when( tdialog ).done(function( event ) {
                $( "#newValue" ).trigger( "focus" );
            });

            $( "#newValue" ).on( "keyup", function( event ) {
                // Remove old Event Handler
                if ( undefined !== keyShortcuts[ options.shortcut ] )
                    $( "body" ).off( "keyup." + keyShortcuts[ options.shortcut ].key );

                // Settings for new Event Handler
                s.settings.key_code = {
                    altKey: event.altKey,
                    ctrlKey: event.ctrlKey,
                    key: event.keyCode,
                    shiftKey: event.shiftKey,
                    action: options.func,
                };
            });

            // Gritter Notification
            //this.gritter( message, "", "fa-header" );
        },

        /* Bind Keyboard Shortcut
        *************************************************************************/
        restoreKeys: function() {
            var f = {},
            key   = "";

            for ( var n in keyShortcuts ) {
                if ( "" === keyShortcuts[ n ] )
                    return false;

                f[ n ] = new Function( keyShortcuts[ n ].action );

                if ( ! keyShortcuts[ n ].action && "function" != typeof f )
                    return false;


                $( "body" ).on( "keyup." + keyShortcuts[ n ].key, function( event ) {
                    event.stopImmediatePropagation();
                    for ( var key in keyShortcuts ) {
                        if ( event.altKey  !== keyShortcuts[ key ].altKey  ||
                            event.ctrlKey  !== keyShortcuts[ key ].ctrlKey ||
                            event.shiftKey !== keyShortcuts[ key ].shiftKey )
                            continue;
                        if ( event.keyCode == keyShortcuts[ key ].key    &&
                            1 > $( ".edit_order_dialog:visible" ).length &&
                            1 > $( "#tdialog" ).length                   &&
                            1 > $( ".addProduct:visible" ).length        &&
                            "TEXTAREA" != $("*:focus").prop( "tagName" ) ) {
                            f[ key ]();
                            break;
                        }
                    }
                });
            }
        }
    },

    /* Order Handler Statistics
    *************************************************************************/
    statistics = {
        init: function() {
            var s                    = this,
            // orders_total             = 0,
            // selectedRows             = $( "#orderTable" ).jtable( "selectedRows" ),
            // numRows                  = selectedRows.length,
            order_handler_statistics = $( "#order_handler_statistics");

            order_handler_statistics.simpleModal({
                closeHTML: "<a href='#' title='Close' class='modal-close'>x</a>",
                close: true,
                persist: true,
                position: ["15%",],
                // minWidth: 1500,
                // minHeight: 600,
                overlayId: 'contact-overlay',
                overlayClose: true,
                containerId: 'statistics-container',
                onOpen: s.open,
                //onShow: s.show,
                onClose: s.close
            }, s);
        },

        open: function(dialog, s) {
            dialog.overlay.fadeIn('fast', function () {
                dialog.container.slideDown('fast', function () {
                    dialog.data.fadeIn('fast', function () {
                        s.show();
                    });
                });
            });
        },

        close: function(dialog) {
            $( "#statistics-container" ).hide( "drop", { direction: "down" }, "fast", function() {
                $.simpleModal.close(); // must call this!
            });
        },

        show: function() {

            function groupByDate(data) {
                var sortedData, result, n, i;

                    sortedData = {};
                    for(i in chartData) {
                        if ( undefined === sortedData[ chartData[i]['date'] ] )
                            sortedData[ chartData[i]['date']] = {order_total: 0, products_purchased: 0};

                        sortedData[ chartData[i]['date'] ].date = chartData[i]['date'];
                        sortedData[ chartData[i]['date'] ].order_total += chartData[i]['order_total'];
                        sortedData[ chartData[i]['date'] ].products_purchased += chartData[i]['products_purchased'];
                    }
                    result = [];
                    for(i in sortedData) {
                        result.push( sortedData[i] );
                    }

                return result;
            }

            var s = this,
            orders_total = 0,
            // chartData = [],
            selectedRows = $( "#orderTable" ).jtable( "selectedRows" ),
            numRows = selectedRows.length,
            order_handler_statistics = $( "#order_handler_statistics");

            chartData = [];

            for ( var i = 0; i < numRows; i++ ) {
                // Create UNIX Timestamp from Date
                var n = numRows - i - 1;

                var newDate = Date.parse( selectedRows.find( "td.date_purchased" )[ n ].innerHTML.replace( /\s/, "T" ) );
                newDate = new Date(newDate);
                newDate = newDate.getFullYear() + "-" + ('0' + (newDate.getMonth() + 1)).slice(-2) + "-" + ('0' + newDate.getDate()).slice(-2) + " " + ('0' + newDate.getHours()).slice(-2) + ":00:00";

                chartData.push({
                    date: new Date(newDate.replace( /\s/, "T" )),
                    products_purchased: parseInt( selectedRows.find( "td.products_purchased" )[ n ].innerHTML, 10 ),
                    order_total: parseFloat( selectedRows.find( "td.order_total" ).eq( i ).text()
                    .replace( /[^0-9\,\.]+/g, "" )
                    .replace( /(,|\.)(?=[^.]*(,|\.))/, "" )
                    .replace( /\D/g, "." )
                    .replace( /\.(?=[^.]*\.)/, "" ) ),
                });
            }

            chartData = groupByDate( chartData );

            //var chartData = generateChartData();

            chart = AmCharts.makeChart("chartdiv",
                {
                    "type": "serial",
                    "pathToImages": "includes/modules/order_handler/amcharts/images/",
                    "categoryField": "date",
                    "startDuration": 1,
                    "dataDateFormat": "YYYY-MM-DD HH",
                    "categoryAxis": {
                        "minPeriod": "hh",
                        "parseDates": true,
                    },
                    "chartCursor": {
                        "categoryBalloonDateFormat": "JJ:NN"
                    },
                    "chartScrollbar": {},
                    "trendLines": [],
                    "graphs": [
                        {
                            "bullet": "round",
                            "id": "AmGraph-1",
                            "balloonText": "[[title]] is [[value]]",
                            "title": "Order Total",
                            "type": "smoothedLine",
                            "valueAxis": "ValueAxis-1",
                            "valueField": "order_total",
                        },
                        {
                            "bullet": "square",
                            "id": "AmGraph-2",
                            "balloonText": "[[value]] [[title]]",
                            "title": "Products Purchased",
                            "type": "smoothedLine",
                            "minimum": 0,
                            "valueAxis": "ValueAxis-2",
                            "valueField": "products_purchased",
                        }
                    ],
                    "guides": [],
                    "valueAxes": [
                        {
                            "id": "ValueAxis-1",
                            "title": "Order Total",
                            "autoGridCount": false,
                            "lineColor": "#FF6600",
                            "position": "left"
                        }, {
                            "id": "ValueAxis-2",
                            "title": "Products Purchased",
                            "lineColor": "#FCD202",
                            "position": "right"
                        }
                    ],
                    "allLabels": [],
                    "balloon": {},
                    "legend": {
                        "switchType": "v",
                        "useGraphSettings": true,
                        "valueAlign": "left"
                    },
                    "titles": [
                        {
                            "id": "Title-1",
                            "size": 15,
                            "text": "Order Handler Statistics"
                        }
                    ],
                    "dataProvider": chartData
                }
            );

            // Fix Chart on Dynamically created Pages
            chart.invalidateSize();
        }
    },

    /* SimpleModal - Mail Customer Modal Popup
    *************************************************************************/
    contact = {
        message: null,
        init: function () {
            $( "body" ).on( "click", "a.mail_customer", function( event ) {
                event.preventDefault();

                // load the contact form using ajax
                $.ajax({
                    url: this.href,
                    //global: false,
                    dataType: "html"
                })
                .done(function( data ){
                    // create a modal dialog with the data
                    $( data ).simpleModal({
                        closeHTML: "<a href='#' title='Close' class='modal-close'>x</a>",
                        position: ["15%",],
                        overlayId: 'contact-overlay',
                        containerId: 'contact-container',
                        onOpen: oHandler.contact.open,
                        onShow: oHandler.contact.show,
                        onClose: oHandler.contact.close
                    });

                    // Initiate Tooltips
                    $( "#simplemodal-data" ).find( ".contact-input" ).qtip({
                        style: {
                            classes: 'qtip-rounded qtip-shadow'
                        },
                        position: {
                            target: 'mouse', // Track the mouse as the positioning target
                            adjust: { x: 5, y: 5 } // Offset it slightly from under the mouse
                        }
                    });
                });
            });
        },
        open: function (dialog) {
            // dynamically determine height
            var h = 296;
            //var h = 445;
            if ($('#contact-subject').length) {
                h += 26;
            }
            if ($('#contact-cc').length) {
                h += 22;
            }

            var title = $('#contact-container .contact-title').html();
            $('#contact-container .contact-title').html(ENTRY_CONTACT_LOADING);
            dialog.overlay.fadeIn(200, function () {
                dialog.container.fadeIn(200, function () {
                    dialog.data.fadeIn(200, function () {
                        $('#contact-container .contact-content').animate({
                            height: h
                        }, function () {
                            $('#contact-container .contact-title').html(title);
                            $('#contact-container form').fadeIn(200, function () {
                                $('#contact-container #contact-name').focus();

                                $('#contact-container .contact-cc').click(function () {
                                    var cc = $('#contact-container #contact-cc');
                                    cc.is(':checked') ? cc.attr('checked', '') : cc.attr('checked', 'checked');
                                });
                            });
                        });
                    });
                });
            });
        },
        show: function (dialog) {
            $('#contact-container .contact-send').click(function (e) {
                e.preventDefault();
                // validate form
                if (oHandler.contact.validate()) {
                    var msg = $('#contact-container .contact-message');
                    msg.fadeOut(function () {
                        msg.removeClass('contact-error').empty();
                    });
                    $('#contact-container .contact-title').html( ENTRY_CONTACT_SENDING );
                    $('#contact-container form').fadeOut(200);
                    $('#contact-container .contact-content').animate({
                        height: '80px'
                    }, function () {
                        $('#contact-container .contact-loading').fadeIn(200, function () {
                            var url = 'contact.php';
                            if ( true === oHandler.configuration.settings.PHPMailer )
                                url += "?php_mailer=1";
                            $.ajax({
                                url: url,
                                data: $('#contact-container form').serialize() + '&action=send',
                                type: 'post',
                                cache: false,
                                //global: false,
                                dataType: 'html',
                                success: function (data) {
                                    $('#contact-container .contact-loading').fadeOut(200, function () {
                                        $('#contact-container .contact-title').html( ENTRY_CONTACT_THANK_YOU );
                                        msg.html(data).fadeIn(200);
                                    });
                                },
                                error: oHandler.contact.error
                            });
                        });
                    });
                }
                else {
                    if ($('#contact-container .contact-message:visible').length > 0) {
                        var msg = $('#contact-container .contact-message div');
                        msg.fadeOut(200, function () {
                            msg.empty();
                            oHandler.contact.showError();
                            msg.fadeIn(200);
                        });
                    }
                    else {
                        $('#contact-container .contact-message').animate({
                            height: '30px'
                        }, oHandler.contact.showError);
                    }

                }
            });
        },
        close: function (dialog) {
            $('#contact-container .contact-message').fadeOut();
            $('#contact-container .contact-title').html( ENTRY_CONTACT_GOODBYE );
            $('#contact-container form').fadeOut(200);
            $('#contact-container .contact-content').animate({
                height: 40
            }, function () {
                dialog.data.fadeOut(200, function () {
                    dialog.container.fadeOut(200, function () {
                        dialog.overlay.fadeOut(200, function () {
                            $.simpleModal.close();
                        });
                    });
                });
            });
        },
        error: function (xhr) {
            $.simpleModal.close();
            return xhr.statusText;
        },
        validate: function () {
            oHandler.contact.message = '';
            if (!$('#contact-container #contact-name').val()) {
                oHandler.contact.message += ENTRY_CONTACT_REQUIRED_NAME;
            }

            var email = $('#contact-container #contact-email').val();
            if (!email) {
                oHandler.contact.message += ENTRY_CONTACT_REQUIRED_EMAIL;
            }
            else {
                if (!oHandler.contact.validateEmail(email)) {
                    oHandler.contact.message += ENTRY_CONTACT_INVALID_EMAIL;
                }
            }

            if (!$('#contact-container #contact-message').val()) {
                oHandler.contact.message += ENTRY_CONTACT_REQUIRED_MESSAGE;
            }

            if (oHandler.contact.message.length > 0) {
                return false;
            }
            else {
                return true;
            }
        },
        validateEmail: function (email) {
            var at = email.lastIndexOf("@");

            // Make sure the at (@) sybmol exists and
            // it is not the first or last character
            if (at < 1 || (at + 1) === email.length)
                return false;

            // Make sure there aren't multiple periods together
            if (/(\.{2,})/.test(email))
                return false;

            // Break up the local and domain portions
            var local = email.substring(0, at);
            var domain = email.substring(at + 1);

            // Check lengths
            if (local.length < 1 || local.length > 64 || domain.length < 4 || domain.length > 255)
                return false;

            // Make sure local and domain don't start with or end with a period
            if (/(^\.|\.$)/.test(local) || /(^\.|\.$)/.test(domain))
                return false;

            // Check for quoted-string addresses
            // Since almost anything is allowed in a quoted-string address,
            // we're just going to let them go through
            if (!/^"(.+)"$/.test(local)) {
                // It's a dot-string address...check for valid characters
                if (!/^[-a-zA-Z0-9!#$%*\/?|^{}`~&'+=_\.]*$/.test(local))
                    return false;
            }

            // Make sure domain contains only valid characters and at least one period
            if (!/^[-a-zA-Z0-9\.]*$/.test(domain) || domain.indexOf(".") === -1)
                return false;

            return true;
        },
        showError: function () {
            $('#contact-container .contact-message')
                .html($('<div class="contact-error"></div>').append(oHandler.contact.message))
                .fadeIn(200);
        }
    },

    /* Add new Field Dialog
    *************************************************************************/
    dialogForm = {
        updateTips: function( t ) {
            tips
            .text( t )
            .addClass( "ui-state-highlight" );
            setTimeout( function() {
                tips.removeClass( "ui-state-highlight", 1500 );
            }, 500 );
        },

        checkLength: function( o, n ) {
            o.removeClass( "ui-state-error" );
            if ( o.val().length <= 0 ) {
                o.addClass( "ui-state-error" );
                this.updateTips( "The " + n + " can not be empty." );
                return false;
            } else {
                return true;
            }
        },

        checkNumber: function( o, n ) {
            if ( isNaN( o.val() ) === true || o.val() === '' ) {
                o.addClass( "ui-state-error" );
                this.updateTips( n );
                return false;
            } else {
                return true;
            }
        },

        init: function() {
            $( "body" ).on( "click", "span.createOrdersTotal", function( event ) {
                event.preventDefault();
                var oID       = $( this ).attr( "data-id" ),
                jtableDataRow = $( "tr.jtable-data-row[data-record-key=" + oID + "]" ).eq( 0 ).next();

                // Initiate Create new field Dialog
                $( "#new_total_oID" ).val( oID );
                $( "#dialog-form" ).dialog( "open" );
            });

            // Initiate Create new field Dialog
            oHandler.dialogForm.dialog();
        },

        dialog: function() {
            var s         = this,
            dialog        = $( "#dialog-form" ),
            cID           = "&cID=" + $.urlParam( "cID" ),
            title         = dialog.find( "#title" ),
            value         = dialog.find( "#value" ),
            allFields     = $( [] ).add( title ).add( value ),
            tips          = dialog.find( "p.validateTips" );

            dialog.dialog( {
                autoOpen: false,
                appendTo: "body",
                height: "auto",
                width: 380,
                modal: oHandler.configuration.settings.showOverlay,
                buttons: {
                    "Add a field": function() {
                        var bValid = true,
                        oID    = $( "#new_total_oID" ).val(),
                        jtableDataRow = $( "tr.jtable-data-row[data-record-key=" + oID + "]" ).eq( 0 ).next();
                        allFields.removeClass( "ui-state-error" );
                        bValid     = bValid && s.checkLength( title, "Title" );
                        bValid     = bValid && s.checkNumber( value, "Value must contain a number." );

                        if ( bValid ) {
                            var form = $( this ).find( "form" ),
                                url = "get_table.php?oID=" + oID + "&" + $( form ).serialize() + cID;

                            if ( edit_order === true ) url += "&edit_order=1";

                            var jqxhr = $.ajax( {
                                type: 'GET',
                                url: decodeURIComponent( url ),
                                async: true,
                                global: ( true === edit_order ? false : true ),
                                dataType: 'json'
                            } );

                            jqxhr.done( function( payload ) {
                                if ( payload.Result == 'OK' ) {
                                    // Send Success Message
                                    oHandler.gritter( "Success", payload.Message, "fa-plus-square-o" );
                                    // Find New Order Totals Table
                                    var products_total_html = $.parseJSON( payload.products_total_html );

                                    // If on 'Edit Order' Page
                                    if ( edit_order === true ) {
                                        products_total_html = $( products_total_html ).find( "#productTotals" ).html();

                                        $( "#productTotals" ).html( products_total_html );
                                    } else {
                                        // jTable Mode
                                        // Order Total Area
                                        products_total_html = $( products_total_html )
                                            .find( "#ordersTotalField" );
                                        jtableDataRow
                                            .find( "#ordersTotalField" )
                                            .html( products_total_html );

                                        // Replace Order Total Value
                                        var order_total       = jtableDataRow.prev().find( "td.order_total" )[0],
                                        newPrice              = $( products_total_html ).find( "#ot_total_value" )[0].innerHTML;

                                        order_total.innerHTML = "<strong>" + newPrice + "</strong>";
                                    }
                                    return false;
                                } else if ( payload.Result == 'ERROR' ) {
                                    // Send Error Message
                                    oHandler.gritter( "Error", payload.Message, "fa-warning" );
                                }
                            } );

                            $( this ).dialog( "close" );
                            $( this ).closest( "div.ui-dialog" ).hide();
                        }
                    },
                    Cancel: function() {
                        $( this ).dialog( "close" );
                        $( this ).closest( "div.ui-dialog" ).hide();
                    }
                },
                close: function() {
                    allFields.val( "" ).removeClass( "ui-state-error" );
                }
            } );
        }
    },

    /* Display Batch Delete Orders modal
    *************************************************************************/
    ajaxOrderDelete = function() {
        $.ajax({
            type: "POST",
            url: encodeURI($( "#batch_orders" ).attr( "action" )),
            data: $( "#batch_orders" ).serialize() + "&batch_delete_x=1",
            //global: false,
            dataType: "html"
        })
        .done(function( data ) {
            $( "#ajaxOrderDelete" ).show().html( data );
            $( "#myModal" ).appendTo("body").modal( "show" );
        });
    },

    /* Batch Delete Orders
    *************************************************************************/
    ajaxDelete = function() {
        var self = this,
            restock = $( "input[name=restock]" ).prop( "checked" ),
            selectedRows = $("#orderTable").jtable("selectedRows"),
            settings = {
                message:  jTableOptions.deleteProggress,
                progress: 0,
                total: selectedRows.length
            };

        jqxhr = $.ajax({
            xhr: function() {
                var xhr = new window.XMLHttpRequest();
                oHandler.showBusy.setup( settings );

                xhr.addEventListener("progress", function( event ) {

                     oHandler.showBusy.update( event.loaded );
                    // if (evt.lengthComputable)
                    // {
                    //     oHandler.showBusy.update( evt.loaded );
                    // }

                }, false);

                return xhr;
            },
            type: "POST",
            cache: false,
            url: "get_table.php?action=delete_confirm",
            data: $( "#formDelete" ).serialize() + ( true === restock ? "&restock=1" : ""),
        });

        jqxhr.done(function( payload ) {
            if( 'number' === typeof payload ) {
                var countOrdersUpdated = payload.toString().length;
                self.gritter( "Success", countOrdersUpdated + " Orders Deleted.", "fa-eraser" );
            }
            selectedRows.each(function () {
                var record = $(this).data('record');
                $('#orderTable').jtable('deleteRecord', {
                    clientOnly: true,
                    key: record.orders_id
                });
            });
        })
        .always(function() {
            $( "#ajaxOrderDelete" ).html( "" );
        });
    },

    /* Replace Content in ID replace_with
    *************************************************************************/
    ajaxLink = function( link, replace_with, global ) {
        // Test link
        if ( "undefined" === typeof link ) return;
        // Replace URL
        history.pushState( null, null, link );

        // Send Request
        $.ajax({
            type: "GET",
            url: encodeURI( link ),
            async: true,
            global: ( typeof( global ) === "undefined" ? true : false ),
        })
        .done(function( data ) {
            // JSON Response equals Error
            if ( typeof data == "object" )
                return gritter( "Error", data.Message );

            // Don't replace element with new Data
            if ( false === replace_with ) return;

            // "Edit Order" page requires different filter
            var replacement = ( true === edit_order ) ? $( data ).find( "#" + replace_with ) : $( data ).closest( "#" + replace_with ),
                pageHeading = $( data ).filter( "#pageHeading" );

            // If nothing found, try again with different filter
            if ( 0 === replacement.length )
                replacement = $( data ).closest( "#" + replace_with );

            // Enable any Comments on Element
            //$( replacement ).find( "span.comments, .tooltip_set" ).tooltip( "toggle" ).tooltip( "hide" );

            // Replace Element with new Element from Response
            $( "#" + replace_with ).replaceWith( replacement );

            // Replace Page Heading if defined
            if ( "undefined" !== typeof pageHeading ) $( "#pageHeading" ).replaceWith( pageHeading );
        });

        return;
    },

    /* Open Ajax Dialog
    *************************************************************************/
    ajaxDialog = function( data_link ) {
        var $dialog = $('<div id="ajax_cart_dialog"></div>')
        .load(data_link, function(){
            $('#overlay').fadeOut('slow', function() {
                $('#overlay').remove();
            });
        })
        .dialog({
            resizable: true,
            modal: oHandler.configuration.settings.showOverlay,
            draggable: true,
            autoOpen: false,
            minWidth: 500,
            dialogClass: "ajax_cart",
            closeOnEscape: true,
            show: {effect: 'fade', duration: 250},
            hide: {effect: 'drop', direction: "down", duration: 200},
            open: function(event, ui)
            {
                //here we can apply unique styling

                $(this).parent()
                .css('top', '15%');

                $(this).parents("div.ui-dialog:first")
                .find("div.ui-dialog-titlebar").remove();

                $(this).parents("div.ui-dialog")
                .css("background", '#FFF');

                $(this).parents("div.ui-dialog:first").find("div.ui-dialog-content")
                .css("padding", 0)
                .css("overflow", 'hidden');
            },
            close: function( event, ui ) {
                var checkout_page = $('form[name=checkout_confirmation]').length,
                    url = $( location ).attr( "href" ).split("/"),
                    filename = url[url.length - 1],
                    addProduct = $( "#add-Product" ),
                    orderTable = $( "#orderTable" );

                addProduct.insertBefore( orderTable );

                filename = filename.split( '?' )[0];
                if ( filename == 'checkout_confirmation.php' || filename == 'shopping_cart.php' || checkout_page !== 0)
                {
                    $('form[name=boxcart_quantity]').submit();
                }

                return $('#ajax_cart_dialog').dialog( "destroy" ).remove();
            }
        }).css('overflow', 'auto').dialog("open");

    },

    /* Capitalize String
    *************************************************************************/
    capitalizeString = function( element ) {
        string = element.val();
        var names = element[0].value.toLowerCase().match(/([^-\s]+[-]{0,1})/g).slice(),
        name = '';

        for ( var i = 0, l = names.length; i < l; i++ ) {
            name += names[ i ].charAt(0).toUpperCase() + names[ i ].slice(1);
            if ( /[^-]$/.test( name ) && i < l - 1 )
            name += " ";
        }
        element.val( name );
        return name;
    },

    /* Format Phone Number
     * Requires Regex Setup
    *************************************************************************/
    formatPhone = function( element ) {
        number = element.val();
        /*number = number.replace(/\D/g, '');

        number = number.replace(/^$/, '$1');
        if ( /^$/.test( number ) ) {
            number = number.replace( /^$/, '' );
        } else {
            number = number.replace( /^$/, '' );
        }*/

        element.val( number );
        return number;
    },

    /* Return tDialog
    *************************************************************************/
    tDialog = function( settings ) {
        var s = this,
            tdialog = $.fn.tdialog({
            type:"prompt",
            showOverlay: oHandler.configuration.settings.showOverlay,
            title:'Enter new value',
            effect:'css3',
            css3EffectIn:'',
            css3EffectOut:'bench-out',
            content: (typeof settings.content != "undefined" ? settings.content : "<h5>Enter new value</h5>"),
            promptCallback:function(){
                var newValue = $("#newValue"),
                newValueVal = newValue.val();
                if ((newValueVal === null) || (newValueVal === '')) return false;

                if ( settings.capitalize.id == 'customers_telephone' && settings.capitalize !== false ) {
                    // Formatting Phone Number
                    // Requires Regex Setup
                    settings.formattedValue = oHandler.formatPhone( newValue );
                    $( settings.capitalize ).html( settings.formattedValue );
                } else if ( "ajaxLinkProduct" != settings.capitalize.className && "customers_email_address" != settings.capitalize.id && "payment_method" != settings.capitalize.id && false !== settings.capitalize ) {
                    settings.formattedValue = oHandler.capitalizeString( newValue );
                    $( settings.capitalize ).html( settings.formattedValue );
                } else {
                    settings.formattedValue = newValueVal;
                    if ( "ajaxLink" == settings.capitalize.className )
                        $( settings.capitalize ).html( settings.formattedValue );
                }

                // Change Customers Name on Row
                if ( "customers_name" == $( settings.capitalize )[0].getAttribute( "data-field" ) ) {
                    $( settings.capitalize )
                        .closest( ".jtable-child-row" )
                        .prev().find( ".customers_name" )
                        .html( settings.formattedValue );
                }

                // Check if Options Field
                if ( settings.extended === true ) {
                    if ( ( newValueVal !== null ) && ( newValueVal !== '' ) ) {
                        setTimeout( function() {
                            var tdialog =
                            $.fn.tdialog({
                                type:"prompt",
                                content:"Price:",
                                title:"Confirm",
                                icon:"confirm",
                                showOverlay: oHandler.configuration.settings.showOverlay,
                                effect:'css3',
                                css3EffectIn:'',
                                css3EffectOut:'bench-out',
                                promptCallback:function(){
                                    newValueVal +=  "&option_price=" + parseFloat( $( "#newValue" ).val() );

                                    settings.url += "&new_value=" + newValueVal;

                                    if ( edit_order === true ) settings.url += "&edit_order=1";

                                    var jqxhr = $.ajax({
                                        type: 'GET',
                                        url: encodeURI( settings.url ),
                                        async: true,
                                        //global: false,
                                        dataType: 'json'
                                    });

                                    s.tDialogRequest( jqxhr, settings );
                                }
                            });

                            $.when( tdialog ).done(function() {
                                var value_pred = settings.capitalize.attributes['data-value-pred'].value;
                                $( "#newValue" ).attr( "placeholder", value_pred ).val( value_pred ).trigger( "focus" );
                            });
                        }, 100);
                        return false;
                    }
                }

                settings.url += "&new_value=" + settings.formattedValue;

                if ( edit_order === true ) settings.url += "&edit_order=1";

                var jqxhr = $.ajax({
                    type: 'GET',
                    url: encodeURI( settings.url ),
                    async: true,
                    //global: false,
                    dataType: 'json'
                });

                s.tDialogRequest( jqxhr, settings );

            }
        });

        $.when( tdialog ).done(function() {
            $( "#newValue" ).attr( "placeholder", settings.pred ).val( settings.pred ).trigger( "focus" );
        });
    },

    /* Handle tDialog Response
    *************************************************************************/
    tDialogRequest = function( jqxhr, settings ) {
        var s = this;
        jqxhr.done(function( payload ) {
            if( 'OK' == payload.Result ) {
                // Send Success Message
                s.gritter( "Success", payload.Message, "fa-thumbs-o-up" );
                if ( settings.refreshTotals === true ) {
                    // Find New Products Table
                    var products_total_html = $.parseJSON( payload.products_total_html );

                    // If on 'Edit Order' Page
                    if ( edit_order === true ) {
                        products_total_html = $( products_total_html ).find( "#productTotals" ).html();

                        return $( "#productTotals" ).html( products_total_html );
                    } else {
                        // jTable Mode
                        var dataField = settings.capitalize.attributes['data-field'].value,
                        jtableDataRow = $( "tr.jtable-data-row[data-record-key=" + settings.orderID + "]" ).eq( 0 ).next();

                        if ( "products_name" !== dataField ) {
                            // Replace Products & Order Total Area
                            jtableDataRow
                                .find( "tr.jtable-child-row" )
                                .children( "td" )
                                .html( $.parseJSON( payload.products_total_html ) )
                                .find( "#tdb-update" ).button({ icons:{ primary:"ui-icon-disk" } }).addClass( "ui-priority-primary" ).parent().removeClass( "tdbLink" );
                            jtableDataRow
                                .find('#change_order_status').multiselect({ buttonClass: 'btn btn-xs btn-info' });

                            var products_purchased = 0,
                            productsRows           = $( products_total_html ).closest( "#productsTable" ).find( "tr.dataTableRow" );

                            // Replace Orders total Product Quantity Field
                            productsRows.each(function() {
                                products_purchased += parseInt( $( this ).find( "span" )[1].attributes['data-pred'].value, 10 );
                            });
                            jtableDataRow.prev().find( "td.products_purchased" ).html( products_purchased );


                            // Replace Order Total Value
                            var order_total       = jtableDataRow.prev().find( "td.order_total" )[0],
                            newPrice              = $( products_total_html ).find( "#ot_total_value" )[0].innerHTML;

                            order_total.innerHTML = "<strong>" + newPrice + "</strong>";
                        } else {
                            $( settings.capitalize ).html( settings.formattedValue );
                            $( settings.capitalize ).attr( "data-pred", settings.formattedValue );
                            return false;
                        }
                        return false;
                    }
                }

            } else if( payload.Result == 'ERROR' ) {
                // Send Error Message
                s.gritter( "Error", payload.Message, "fa-thumbs-o-down" );
            }
        });
    },

    /* Return json_message with jQuery Gritter
    *************************************************************************/
    json_message = function( jqxhr, settings ) {
        var self = this,
        config   = {
            Message: "",
            Title: "Success",
        };
        $.extend( config, settings );

        jqxhr.done(function( payload ) {
            $.extend( config, payload );
            if( 'OK' == config.Result ) {
                self.gritter( config.Title, config.Message );
            } else if( "ERROR" == config.Result, "fa-thumbs-o-up" ) {
                self.gritter( "Error", config.Message, "fa-thumbs-o-down" );
            }
        });
    },

    /* Update Shipping Method
    *************************************************************************/
    updateShippingMethod = function( e, settings ) {
        var jqxhr =
            $.ajax({
                type: 'GET',
                url: encodeURI( settings.url_title ),
                async: true,
                dataType: 'json'
            });

        var s = this;
        jqxhr.done(function( payload ) {
            if( 'OK' == payload.Result ) {
                // Send Success Message
                s.gritter( "Success", payload.Message, "fa-thumbs-o-up" );
                if ( settings.refreshTotals === true ) {

______________________________________________________

 

* End Part 5 *

______________________________________________________

Link to comment
Share on other sites

Replace ./admin/includes/modules/order_handler/js/01_03_order_handler_rev3_module.js With This: (1/2 Part of this File)

                    // Find New Products Table
                    var products_total_html = $.parseJSON( payload.products_total_html );

                    // If on 'Edit Order' Page
                    if ( edit_order === true ) {
                        products_total_html = $( products_total_html ).find( "#productTotals" ).html();

                        return $( "#productTotals" ).html( products_total_html );
                    } else {
                        // jTable Mode
                        var jtableDataRow = $( "tr.jtable-data-row[data-record-key=" + settings.orderID + "]" ).eq( 0 ).next();

                        // Replace Products & Order Total Area
                        jtableDataRow
                            .find( "tr.jtable-child-row" )
                            .children( "td" )
                            .html( $.parseJSON( payload.products_total_html ) )
                            .find( "#tdb-update" ).button({ icons:{ primary:"ui-icon-disk" } }).addClass( "ui-priority-primary" ).parent().removeClass( "tdbLink" );
                        jtableDataRow
                            .find('#change_order_status').multiselect({ buttonClass: 'btn btn-xs btn-info' });

                        var products_purchased = 0,
                        productsRows           = $( products_total_html ).closest( "#productsTable" ).find( "tr.dataTableRow" );

                        // Replace Orders total Product Quantity Field
                        productsRows.each(function() {
                            products_purchased += parseInt( $( this ).find( "span" )[1].attributes['data-pred'].value, 10 );
                        });
                        jtableDataRow.prev().find( "td.products_purchased" ).html( products_purchased );


                        // Replace Order Total Value
                        var order_total       = jtableDataRow.prev().find( "td.order_total" )[0],
                        newPrice              = $( products_total_html ).find( "#ot_total_value" )[0].innerHTML;

                        order_total.innerHTML = "<strong>" + newPrice + "</strong>";

                        var shipping_method = $( "#shipping_method_pull_down" ).val(),
                            selected = $( "#shipping_method_pull_down_" + settings.orderID ).find( "option" ).filter(function () { return this.value == shipping_method; }).val();

                        $( "#shipping_method_pull_down_" + settings.orderID )
                            .multiselect( 'select', selected )
                            .multiselect( 'refresh' );
                        return false;
                    }
                }

            } else if( payload.Result == 'ERROR' ) {
                // Send Error Message
                s.gritter( "Error", payload.Message, "fa-thumbs-o-down" );
            }
        });

        //this.json_message( jqxhr, settings );
    },

    /* Update Payment Method
    *************************************************************************/
    updatePaymentMethod = function( e, settings ) {
        var jqxhr =
            $.ajax({
                type: 'GET',
                url: encodeURI( settings.url_title ),
                async: true,
                dataType: 'json'
            });

        jqxhr.done(function( payload ) {
            // Find New Products Table
            var products_total_html = $.parseJSON( payload.products_total_html );

            // If on 'Edit Order' Page
            if ( edit_order === true ) {
                products_total_html = $( products_total_html ).find( "#productTotals" ).html();

                return $( "#productTotals" ).html( products_total_html );
            } else {
                // jTable Mode
                var jtableDataRow = $( "tr.jtable-data-row[data-record-key=" + settings.orderID + "]" ).eq( 0 ).next();

                // Replace Products & Order Total Area
                jtableDataRow
                    .find( "tr.jtable-child-row" )
                    .children( "td" )
                    .html( $.parseJSON( payload.products_total_html ) )
                    .find( "#tdb-update" ).button({ icons:{ primary:"ui-icon-disk" } }).addClass( "ui-priority-primary" ).parent().removeClass( "tdbLink" );
                jtableDataRow
                    .find('#change_order_status').multiselect({ buttonClass: 'btn btn-xs btn-info' });

                var products_purchased = 0,
                productsRows           = $( products_total_html ).closest( "#productsTable" ).find( "tr.dataTableRow" );

                // Replace Orders total Product Quantity Field
                productsRows.each(function() {
                    products_purchased += parseInt( $( this ).find( "span" )[1].attributes['data-pred'].value, 10 );
                });
                jtableDataRow.prev().find( "td.products_purchased" ).html( products_purchased );


                // Replace Order Total Value
                var order_total       = jtableDataRow.prev().find( "td.order_total" )[0],
                newPrice              = $( products_total_html ).find( "#ot_total_value" )[0].innerHTML;

                order_total.innerHTML = "<strong>" + newPrice + "</strong>";

                var payment_method = ( 0 < $( "#payment_method_pull_down" ).length ? JSON.parse(  $( "#payment_method_pull_down" ).val() )[1] : JSON.parse(  $( "#payment_method_pull_down_" + settings.orderID ).val() )[1] ),
                selected = $( "#payment_method_pull_down_" + settings.orderID ).find( "option" ).filter(function () { return JSON.parse( this.value )[1] == payment_method; } ).val();

                $( "#payment_method_pull_down_" + settings.orderID )
                    .multiselect( 'select', selected )
                    .multiselect( 'refresh' );
                return false;
            }

        });

        this.json_message( jqxhr, settings );
    },

    /* Debug Page Load Time
    *************************************************************************/
    ajaxLogging = function( start, pageAction, logOnly ) {
        var stop          = Date.now();
        var executionTime = Number( stop - start ) / 1000 + " s";
        //var t1            = performance.now();

        // Log Execution Time to Console
        //console.log("Call to jquery took " + (t1 - t0) + " milliseconds.");
        console.log( ( undefined !== pageAction ? pageAction + " executed in: " : "Code executed in: " ) + executionTime );

        // Send Gritter Notification
        if ( false !== oHandler.config.toggleGritter )
            this.gritter( "Page Load Time", executionTime );

        if ( undefined !== logOnly ) return false;

        // Send To Page Parse Time Log
        var jqxhr =
            $.ajax({
                type: 'POST',
                url: "get_table.php?action=ajax_logging",
                data: { time: executionTime },
                global: false,
                dataType: 'json'
            });
    },

    /* jQuery Gritter - Growl like notifications
    *************************************************************************/
    gritter = function( title, notification, imageCSS ) {
        // Check Configuration if Gritter is Disabled
        if ( false === oHandler.config.toggleGritter )
            return false;

        $.gritter.add({
            // (string | mandatory) the heading of the notification
            title: title,
            // (string | mandatory) the text inside the notification
            text: notification,
            // Icon Class - Added for Advanced Order Handler Rev3
            imageCSS: ( undefined !== imageCSS ? imageCSS : "fa-cog" + ( true === polling ? " fa-spin" : "" ) ),
            // (bool | optional) if you want it to fade out on its own or just sit there
            sticky: false,
            // (function | optional) function called before it opens
            before_open: function(){
                $( "#gritterRemove" ).animate( { opacity: 1 }, 400 );
            },
            // (function | optional) function called after it opens
            after_open: function(e){
                // if ( stopper === true )
                //     $( e ).find( "i.fa-cog" ).removeClass( "cog-spin" );
            },
            after_close: function(){
                if ( 1 >= $( "#gritter-notice-wrapper").find( "div.gritter-item-wrapper" ).length )
                    $( "#gritterRemove" ).animate( { opacity: 0 }, 400 );
            }
        });
    },

    /* contextMenu - Right Click Menu
    *************************************************************************/
    context_menu = function() {
        //return false;
        $( "#jTable" ).contextMenu({
            selector: 'tr.contextMenu',
            type: 'text',
            callback: function(key, options) {
                //if ( 0 !== event.button ) return false;
                var orders_id  = $( this ).attr( "data-record-key" ),
                action         = key,
                link           = document.location.origin + document.location.pathname,
                type           = "GET",
                data           = "",
                orderTable     = $( "#orderTable" ).size(),
                row            = $( this ),
                selectedRows   = $('#orderTable').jtable('selectedRows'),
                customers_name = $( row ).find( "td.customers_name" ).text();

                if ( "expand_order" == action ) {
                    $( this ).find( "td.expand_order" ).find( "span" ).click();
                    return;
                } else if ( "delete" == action ) {
                    //link = document.location.origin + document.location.pathname;
                    link += "?action=delete_order";
                    data = { orders_id: orders_id };
                    type = "POST";
                } else if ( "edit_order" == action ) {
                    link = $( this ).find( "td" )[1].children[0].href;
                    // Replace URL
                    history.pushState( null, null, link );
                } else if ( "duplicate_order" == action ) {
                    link = $( this ).find( "a.duplicate_order" )[0].href;
                } else if ( "mail_confirmation" == action ) {
                    link = $( this ).find( "a.mail_confirmation" )[0].href;
                    if ( true === oHandler.configuration.settings.PHPMailer )
                        link += "&php_mailer=1";
                }


                jqxhr = $.ajax({
                    type: type,
                    beforeSend: function( xhr ) {
                        settings = {
                            message: jTableOptions.loadingMessage,
                        };
                        if ( "edit_order" != action )
                            oHandler.showBusy.setup( settings );
                    },
                    url: link,
                    data: data
                });


                jqxhr.done(function( data ) {
                    if ( "edit_order" == action ) {
                        var replacement = $( data ).closest( '#orderTable' ),
                        pageHeading = $( data ).closest( "#pageHeading" );
                        edit_order = true;
                        //$( replacement ).find('span.comments, .tooltip_set').tooltip('toggle').tooltip('hide');
                        $( '#pageHeading' ).replaceWith( pageHeading );
                        $( '#orderTable' ).replaceWith( replacement );
                        return false;
                    } else if ( "delete" == action ) {
                        $('#orderTable').jtable('deleteRecord', {
                            clientOnly: true,
                            key: orders_id
                        });
                        return oHandler.gritter( "Order Deleted", "Order <strong>" + orders_id + "</strong> from <strong>" + customers_name + "</strong> Deleted.", "fa-erase" );
                    } else if ( "mail_confirmation" == action ) {
                        if( 'OK' == data.Result ) {
                            oHandler.gritter( "Success", data.Message, "fa-envelope-o" );
                        } else if( data.Result == 'ERROR' ) {
                            oHandler.gritter( "Error", data.Message, "fa-thumbs-o-down" );
                        }
                        return false;
                    }

                    if ( 'OK' == data.Result ) {
                        if (  0 < orderTable ) {
                            $('#orderTable').jtable('addRecord', {
                                index: 0,
                                clientOnly: true,
                                record: data.Records[0]
                            });
                            if ( data.Records[0].orders_id > last_order_number )
                                last_order_number = data.Records[0].orders_id;
                        }
                        oHandler.gritter( "Order Duplicated", data.Message, "fa-thumbs-o-up" );
                    } else if( 'ERROR' == data.Result ) {
                        oHandler.gritter( "Error", data.Message, "fa-thumbs-o-down" );
                    }
                    return false;
                });

                jqxhr.always(function() {
                    if ( "edit_order" != action )
                        oHandler.showBusy.hide();
                });


            },
            items: {
                "expand_order": {name: "Expand Order", icon: "edit"},
                "edit_order": {name: "Edit", icon: "edit"},
                "duplicate_order": {name: "Duplicate", icon: "cut"},
                "mail_confirmation": {name: "Send Mail", icon: "mail"},
                "delete": {name: "Delete", icon: "delete"}
            }
        });
    },

    /* Select Product from Add Product Menu
    *************************************************************************/
    selectProduct = function( orderID, prID, prName ) {
        var s      = this,
        parentRow  = ( true === edit_order ?
            $( "#orderTable" ) :
            $( "tr.jtable-data-row[data-record-key=" + orderID + "]" )
            .eq( 0 ).next() ),
        url        ="get_table.php?action=attributes&prID=" + prID,
        jqxhr      = $.ajax({
            type: 'GET',
            url: encodeURI( url ),
            //global: false,
            dataType: 'html',
        });

        parentRow.find( "#addProdName" ).html( prName );
        parentRow.find( "#addProductFind" ).show();

        jqxhr.done(function( data ) {
            var form =
            $( '<form name="attributes" id="attributes" action=""></form>' )
                .append( '<input type="hidden" name="products_id" value="' + prID + '">')
                .append( data )
                .submit(function( event ) {
                    event.preventDefault();
                    oHandler.setAttr( parentRow, orderID );
                    return false;
                });

            form.find( "button" ).hover(
              function() {
                this.classList.add( "ui-state-hover" );
            }, function() {
                this.classList.remove( "ui-state-hover" );
            });

            parentRow.find( "#ProdAttr" ).html( form );
        });

    },

    /* Hide Add Product Modal
    *************************************************************************/
    hideAddProducts = function() {
        $( "#add-Product" ).hide();
        $( "#addProdName" ).html( "" );
        $( "#ProdAttr" ).html( "" );
        $( "#keywords" ).val( "" );
    },

    /* Add Attribute to Product
    *************************************************************************/
    setAttr = function( parentRow, orderID ) {
        var s     = this,
        orderID   = ( undefined === typeof orderID ) ? $( "#order_number" ).html() : orderID,
        url       ="get_table.php?action=set_attributes&oID=" + orderID,
        postVar   = "products_quantity=";

        $.fn.tdialog({
            type:"prompt",
            content:"Quantity?",
            title:"Confirm",
            icon:"confirm",
            showOverlay: oHandler.configuration.settings.showOverlay,
            effect:'css3',
            css3EffectIn:'',
            css3EffectOut:'bench-out',
            promptCallback:function(){

                postVar += $( "#newValue").val();
                for (var i = 0; i < parentRow.find( "#attributes" )[0].elements.length; i++){
                    postVar += '&' + document.attributes.elements[i].name + '=' + document.attributes.elements[i].value;
                }

                if ( edit_order === true ) url += "&edit_order=1";

                var jqxhr = $.ajax({
                    type: 'POST',
                    url: encodeURI( url ),
                    data: postVar,
                    //global: false,
                    dataType: 'json'
                });

                jqxhr.done(function( payload ) {
                    //$( this ).hideAddProducts();
                    s.hideAddProducts();
                    if( 'OK' == payload.Result ) {
                        oHandler.gritter( "Success", payload.Message, "fa-copy" );
                        var products_total_html = $.parseJSON( payload.products_total_html );
                        if ( edit_order === true ) {
                            products_total_html = $( products_total_html ).find( "#productTotals" ).html();
                            $( "#productTotals" ).html( products_total_html );
                        } else {
                            var products_purchased = 0,
                            productsRows           = $( products_total_html ).closest( "#productsTable" ).find( "tr.dataTableRow" );

                            // Replace Products & Order Total Area
                            parentRow
                                .find( "#add-Product" )
                                .closest( "tr.jtable-child-row" )
                                .children( "td" )
                                .html( $.parseJSON( payload.products_total_html ) )
                                .find( "#tdb-update" )
                                .button({ icons:{ primary:"ui-icon-disk" } })
                                .addClass( "ui-priority-primary" ).parent()
                                .removeClass( "tdbLink" );
                            parentRow
                                .find('#change_order_status')
                                .multiselect({ buttonClass: 'btn btn-xs btn-info' });

                            // Replace Order Total Product Quantity Field
                            productsRows.each(function() {
                                products_purchased += parseInt( $( this ).find( "span" )[1].attributes['data-pred'].value, 10 );
                            });
                            parentRow.prev().find( "td.products_purchased" ).html( products_purchased );

                            // Replace Order Total Value
                            var order_total = parentRow.prev().find( "td.order_total" )[0],
                            newPrice        = $( products_total_html ).find( "#ot_total_value" )[0].innerHTML;

                            order_total.innerHTML = "<strong>" + newPrice + "</strong>";
                        }
                        return false;
                    } else if( payload.Result == 'ERROR' ) {
                        oHandler.gritter( "Error", payload.Message, "fa-thumbs-o-down" );
                    }
                });
            }
        });

        $.when( tdialog ).done(function(){
            $( "#newValue" ).val( 1 ).trigger( "focus" );
        });
    },

// (C) Dylan Knutson 2012
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

    scrollNav = function( self, opts ) {
        var
            window_scroll = $(window).scrollTop(),
            navbar = self,
            navbar_height = navbar.height(),
            scroll_last = window_scroll,
            navbar_visible = navbar_height;

        var resize_handler = function(event) {
            navbar_height = navbar.height();
        };

        $(window).resize(resize_handler);

        $(window).scroll(function(event) {
            //calculate how far the window was scrolled
            //scrolling up the page is a positive delta
            window_scroll = $(window).scrollTop();
            var
                scroll_delta = scroll_last - window_scroll,
                navbar_visible_new = navbar_visible + scroll_delta;


            if(scroll_delta < 0) {
                //scrolling down the page
                if(navbar_visible == navbar_height) {
                    //navbar currently is totally visible, and has fixed positioning set
                    //set to abs positioning so it begins to go out of frame
                    if(window_scroll < 0) window_scroll = 0;
                    navbar.css({"position": "absolute", "top": window_scroll + "px"});
                }
                //else:
                //navbar will be partially visible, let abs positioning move it
            }
            else if(scroll_delta > 0) {
                if(navbar_visible <= 0) {
                //navbar was not visible, set abs positioning right above this
                    navbar.css({"position": "absolute", "top": (window_scroll - navbar_height) + "px"});
                }
                //scrolling up the page
                if(navbar_visible_new >= navbar_height) {
                    //navbar will be 100% visible
                    navbar.css({"position": "fixed", "top": "0px"});
                }
            }

            //recalculate the amount the navbar is visible
            navbar_visible = Math.min(Math.max(navbar_visible_new, 0), navbar_height);
            scroll_last = window_scroll;
        });
    },

    /* Animate Header Title
    *************************************************************************/
    initHeader = function() {

        $( "#pageHeading" ).find( "h1" ).css( "visibility", "visible" ).addClass('bounceInUp animated').one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function(){
          $(this).removeClass();
        });
    },

    /* Show Tooltips
    *************************************************************************/
    showTooltips = function() {
        // Tooltips for #navigationTop
        $( "#navigationTop" ).find( '.tooltip_set').qtip({
            style: {
                classes: 'qtip-bootstrap',
            },
            content: {
                title: function( event, api ) {
                    return $( "#navigationTop" ).attr( "data-tooltip-title" );
                }
            },
            position: {
                my: 'top left',
                at: 'bottom center',
                target: 'event',
                viewport: $(window)
            },
            show: {
                delay: 800,
            },
            hide: {
                distance: 30
            }
        });

        // Tooltips for #navigationBottom
        $( "#navigationBottom" ).find( '.tooltip_set').qtip({
            style: {
                classes: 'qtip-bootstrap',
            },
            content: {
                title: function( event, api ) {
                    return $( "#navigationBottom" ).attr( "data-tooltip-title" );
                }
            },
            position: {
                my: 'bottom left',
                at: 'top center',
                target: 'event',
                viewport: $(window)
            },
            show: {
                delay: 800,
            },
            hide: {
                distance: 30
            }
        });

        // Tooltips for Action Field / Orders / Table Rows
        $( "#jTable" ).find( '.tooltip_set').qtip({
            style: {
                classes: 'qtip-bootstrap',
            },
            position: {
                viewport: $(window)
            },
            show: {
                effect: function() {
                    $(this).fadeIn(400);
                }
            },
            hide: {
                distance: 30,
                effect: function() {
                    $(this).fadeOut(400);
                }
            }
        });

        // Tooltips for Configuration Modal
        $( "#configModal" ).find( 'li').qtip({
            style: {
                classes: 'qtip-bootstrap',
            },
            content: {
                title: function( event, api ) {
                    return $( this ).find( "label" ).html();
                },
                button: true
            },
            position: {
                my: 'top left',
                at: 'bottom left',
                target: 'event',
                viewport: $(window)
            },
            show: {
                solo: true
            },
            hide: {
                distance: 400
            }
        });

        // Gritter Notification
        this.gritter( "Tooltips Enabled", "", "fa-comments-o" );
    },

    /* Ajax Long Polling for new Orders
    *************************************************************************/
    messagesLongPolling = {

        buttons: {
            enableButton: $( "#enable_button" ),
            disableButton: $( "#disable_button" )
        },

        init: function( event ) {
            //this.bindUIActions();
        },

        start: function() {
            var s = this;
            // Test if Polling is activated
            if ( polling === false ) return setTimeout( function(){ s.start( last_order_number ); },1000);
            // Abort Request
            if ( undefined !== ajaxRequest )
                ajaxRequest.abort();
            ajaxRequest =
            $.ajax({
                url: 'stream_rev3.php',
                type: 'GET',
                data: 'oID=' + last_order_number + '&poll_timer=' + $( "#poll_timer" ).val(),
                global: false,
                async: true,
                cache: false,
                dataType: 'json',
                success: function( payload ){
                    if ( payload.Result == 'no-results' ) {
                        setTimeout( function(){ s.start( last_order_number ); },1000);
                        return false;
                    } else if ( 'OK' == payload.Result ){
                        $( payload.Records ).each(function () {
                            if ( this.orders_id <= last_order_number ) return false;
                            $('#orderTable').jtable('addRecord', {
                                index: 0,
                                clientOnly: true,
                                record: this
                            });

                            // Activate Bootstrap Multiselect on new Row
                            var new_row = $( "#jTable" ).find( "tr.jtable-data-row" ).eq(0);
                            new_row.find( "select" ).multiselect({ buttonClass: 'btn btn-xs btn-info' });

                            oHandler.gritter( "New Order", this.customers_name + " placed a new order for " + this.order_total, "fa-plus-square-o" );
                            if ( this.orders_id > last_order_number )
                                last_order_number = this.orders_id;

                            // Enable Tooltips on new Row
                            //$( "#jTable" ).find( "tr.jtable-data-row" ).eq( 0 ).find( "[data-original-title]" ).tooltip( "toggle" ).tooltip( "hide" );
                        });
                    }
                    setTimeout( function(){ s.start( last_order_number ); },1000);
                    return false;
                },
                error: function(){
                    setTimeout( function(){ s.start( last_order_number ); },15000);
                }
            });
        },

        toggle: function( event, switchOn ) {
            var s = this,
            target;
            if ( true === switchOn ) {
                target = s.buttons.enableButton[0];
            } else if ( false === switchOn ) {
                target = s.buttons.disableButton[0];
            } else {
                target = event.currentTarget;
            }

            var parent = target.parentNode,
            classes    = target.className,
            success    = target.parentElement.children[2],
            danger     = target.parentElement.children[3],
            cog        = target.parentElement.children[0].children[0];

            if ( target == success ) {
                // Start Spinning Cog icon
                cog.className = "fa fa-cog cog-spin";
                target.className = "btn btn-lg btn-success";
                danger.className = "btn btn-lg btn-default";
                oHandler.gritter( "Ajax Long-Polling", "Polling Enabled.", "fa-cog fa-spin" );
                polling = true;
                if ( true === switchOn )
                    s.start();
            } else if ( target == danger) {
                // Stop Spinning Cog icon
                cog.className = "fa fa-cog";
                target.className = "btn btn-lg btn-danger active";
                success.className = "btn btn-lg btn-default";
                polling = false;
                oHandler.gritter( "Ajax Long-Polling", "Polling Disabled.", "fa-cog" );
                if ( ajaxRequest !== undefined )
                    ajaxRequest.abort();
            }
        },

        bindUIActions: function( event ) {
            var s = this;
            //s.buttons.enableButton.on( "click", function( event ) {
            $( "body" ).on( "click", "#enable_button", function( event ) {
                s.toggle( event );
                s.start();
            });
            //s.buttons.disableButton.on( "click", function( event ) {
            $( "body" ).on( "click", "#disable_button", function( event ) {
                if ( false === polling )
                    return false;
                s.toggle( event );
            });
        }

    },

    /* Event Handlers
    *************************************************************************/
    bindEventHandlers = {

        buttons: {
            // Click Handlers
            batchDelete: $( "#batch_delete" ),
            batchConfirm: $( "#batch_confirm" ),
            childTable2: $( "#orderTable span.childTable2" ),
            closeWindow: $( "#ajax_cart_top button.closeWindow" ),
            expandTable: $( "#navigationTop" ).find( "i.expand_table" ),
            gritterRemove: $( "#gritterRemove" ),
            logoutButton: $( "#logout-button" ),
            submitButton: $( "#navigationBottom" ).find( "a.submit_button" ),
            refreshButton: $( "#refresh-button" ),

            // Submit Handlers
            selectStatus: $( "#select_status" ),
            batchOrders: $( "#contentText #batch_orders" ),

            // Change Handlers
            //paymentMethodPullDown: $( "#payment_method_pull_down" ),
            jsSwitch: $( "#navigationTop" ).find( "input.js-switch" ),

            tax_value: $( "#tax_value" ),
            autostatus: $( "#autostatus" ),
            keywords: $( "#keywords" ),
            context_menu: $( "#context_menu" ),
            configModal: $( "#configModal" ).find( "input.configButton" ),
            status: $( "#status" ),
            searchOrders: $( "#search_orders" )
        },

        init: function() {
            this.bindClickActions();
            this.popstate();
            this.ajaxSetup();
        },

        bindClickActions: function() {
            var s = this;
            $( "body" ).on( "click", "#orderTable span.ajaxLink", function( event ) {
                event.preventDefault();
                var orderID = ( edit_order === true ) ? $( "#order_number" ).text() : $( this ).closest( "tr" ).attr( "data-record-key" ),
                table       = $( this ).attr( "data-table" ),
                field       = $( this ).attr( "data-field" ),
                pred        = $( this ).html(),
                id          = this.id,
                url         = "get_table.php?action=update_order_field&oID=" + orderID + "&db_table=" + table + "&field=" + field + "&new_value=";

                oHandler.tDialog( { url: url, orderID: orderID, pred: pred, extended: false, refreshTotals: false, capitalize: this } );
            });


            // Update/Remove Order Total Field
            $( 'body' ).on( 'click', '#orderTable span.ajaxLinkTotals', function( event ) {
                event.preventDefault();

                var orderID   = ( edit_order === true ) ? $( "#order_number" ).text() : $( this ).closest( "tr.jtable-child-row" ).prev().attr( "data-record-key" ),
                cID           = "&cID=" + $.urlParam( "cID" ),
                jtableDataRow = $( "tr.jtable-data-row[data-record-key=" + orderID + "]" ).eq( 0 ).next(),
                classe        = $( this ).attr( "data-class" ),
                column        = $( this ).attr( "data-column" ),
                pred          = $( this ).attr( "data-pred" ),
                action        = $( this ).attr( "data-action" ),
                data_title    = $( this ).attr( "data-title" ),
                href          = $( this ).attr( "data-href" ),
                jqxhr         = '';

                if ( true === edit_order ) href += "&edit_order=1";

                if ( action == "eliminate" ) {
                    jqxhr =
                        $.ajax( {
                            type: 'POST',
                            async: true,
                            // global: false,
                            contentType: "application/x-www-form-urlencoded; charset=utf-8",
                            data: {
                                oID: orderID,
                                total_class: classe,
                                title: encodeURIComponent( data_title )
                            },
                            url: encodeURI( href ),
                            dataType: "JSON"
                        } );

                    jqxhr.done( function( payload ) {
                        if ( payload.Result == 'OK' ) {
                            // Send Success Message
                            oHandler.gritter( "Success", payload.Message, "fa-eraser" );
                            // Find New Order Totals Table
                            var products_total_html = $.parseJSON( payload.products_total_html );

                            // If on 'Edit Order' Page
                            if ( edit_order === true ) {
                                products_total_html = $( products_total_html ).find( "#productTotals" ).html();

                                $( "#productTotals" ).html( products_total_html );
                            } else {
                                // jTable Mode
                                // Replace Products & Order Total Area
                                jtableDataRow
                                    .find( "tr.jtable-child-row" )
                                    .children( "td" )
                                    .html( $.parseJSON( payload.products_total_html ) )
                                    .find( "#tdb-update" ).button({ icons:{ primary:"ui-icon-disk" } }).addClass( "ui-priority-primary" ).parent().removeClass( "tdbLink" );
                                jtableDataRow
                                    .find('#change_order_status').multiselect({ buttonClass: 'btn btn-xs btn-info' });

                                var products_purchased = 0,
                                productsRows           = $( products_total_html ).closest( "#productsTable" ).find( "tr.dataTableRow" );

                                // Replace Order Total Product Quantity Field
                                productsRows.each(function() {
                                    products_purchased += parseInt( $( this ).find( "span" )[1].attributes['data-pred'].value, 10 );
                                });
                                jtableDataRow.prev().find( "td.products_purchased" ).html( products_purchased );


                                // Replace Order Total Value
                                var order_total       = jtableDataRow.prev().find( "td.order_total" )[0],
                                newPrice              = $( products_total_html ).find( "#ot_total_value" )[0].innerHTML;

                                order_total.innerHTML = "<strong>" + newPrice + "</strong>";
                            }
                            return false;
                        } else if ( payload.Result == 'ERROR' ) {
                            oHandler.gritter( "Error", payload.Message, "fa-thumbs-o-down" );
                        }
                    } );
                    return false;
                }

                if ( column == "value" )
                    pred = pred.replace( /[^0-9\.\,]+/g, "" );

                var tdialog = $.fn.tdialog( {
                    type: "prompt",
                    title: 'Enter new value',
                    closeKey: true,
                    showOverlay: oHandler.configuration.settings.showOverlay,
                    content: ( typeof content != "undefined" ? content : "<h5>Enter new value</h5>" ),
                    promptCallback: function() {
                        var newValue = $( "#newValue" ).val();

                        if ( newValue !== null ) {
                            if ( classe == 'value' ) newValue = parseFloat( newValue );
                            var url = "get_table.php?action=orders_total_update&oID=" + orderID + "&column=" + column + "&class=" + classe + "&new_value=" + newValue + cID;

                            if ( true === edit_order ) url += "&edit_order=1";
                            if ( "undefined" != typeof data_title ) url += "&title=" + data_title;

                            jqxhr = $.ajax( {
                                type: 'GET',
                                url: encodeURI( url ),
                                async: false,
                                global: false,
                                dataType: 'json'
                            } );

                            jqxhr.done( function( payload ) {
                                if ( payload.Result == 'OK' ) {
                                    // Send Success Message
                                    oHandler.gritter( "Success", payload.Message, "fa-thumbs-o-up" );
                                    // Find New Order Totals Table
                                    var products_total_html = $.parseJSON( payload.products_total_html );

                                    // If on 'Edit Order' Page
                                    if ( edit_order === true ) {
                                        var products_total_html = $.parseJSON( payload.products_total_html );

                                        products_total_html = $( products_total_html ).find( "#productTotals" ).html();

                                        return $( "#productTotals" ).html( products_total_html );
                                    } else {
                                        // Replace Products & Order Total Area
                                        jtableDataRow
                                            .find( "tr.jtable-child-row" )
                                            .children( "td" )
                                            .html( $.parseJSON( payload.products_total_html ) )
                                        .find( "#tdb-update" ).button({ icons:{ primary:"ui-icon-disk" } }).addClass( "ui-priority-primary" ).parent().removeClass( "tdbLink" );
                                        jtableDataRow
                                            .find('#change_order_status').multiselect({ buttonClass: 'btn btn-xs btn-info' });

                                        // Replace Order Total Value
                                        var order_total       = jtableDataRow.prev().find( "td.order_total" )[0],
                                        newPrice              = $( products_total_html ).find( "#ot_total_value" )[0].innerHTML;

                                        order_total.innerHTML = "<strong>" + newPrice + "</strong>";
                                    }
                                } else if ( payload.Result == 'ERROR' ) {
                                    oHandler.gritter( "Error", payload.Message, "fa-thumbs-o-down" );
                                }
                            } );

                            return false;
                        }
                    }
                } );

                $.when( tdialog ).done( function() {
                    $( "#newValue" ).attr( "placeholder", pred ).val( pred ).trigger( "focus" );
                } );
                return false;
            } );

            $( "body" ).on( "click", "#productTotals span.ajaxLinkProduct", function( event ) {
                event.preventDefault();

                var orderID   = ( edit_order === true ) ? $( "#order_number" ).text() : $( this ).closest( "tr.jtable-child-row" ).prev().attr( "data-record-key" ),
                cID           = "&cID=" + $.urlParam( "cID" ),
                productID     = this.attributes['data-product'].value,
                field         = this.attributes['data-field'].value,
                action        = this.attributes['data-action'].value,
                extra         = this.attributes['data-extra'].value,
                pred          = this.attributes['data-pred'].value,
                newValue      = "",
                url           = "",
                productTotals = $( this ).closest( "#productTotals" ).find( "tr.jtable-child-row" ).children( "td" );

                if ( "eliminate" == action ) {
                    $.fn.tdialog( {
                        type: "confirm",
                        content: "Are you sure you want to eliminate this product from the order?",
                        title: "Confirm",
                        icon: "confirm",
                        showOverlay: oHandler.configuration.settings.showOverlay,
                        effect: "css3",
                        css3EffectIn: "",
                        css3EffectOut: "bench-out",
                        confirmCallback: function() {
                            newValue = 0;
                            url = "get_table.php?action=eliminate&pID=" + productID + "&oID=" + orderID + "&new_value=" + newValue + cID;

                            if ( edit_order === true ) url += "&edit_order=1";

                            var jqxhr = $.ajax( {
                                type: "GET",
                                url: encodeURI( url ),
                                // global: false,
                                dataType: "json"
                            } );

                            jqxhr.done( function( payload ) {
                                if ( payload.Result == "OK" ) {
                                    // Send Success Message
                                    oHandler.gritter( "Success", payload.Message, "fa-eraser" );
                                    // Find New Order Totals Table
                                    var products_total_html = $.parseJSON( payload.products_total_html );
                                    // If on 'Edit Order' Page
                                    if ( edit_order === true ) {
                                        products_total_html = $( products_total_html ).find( "#productTotals" ).html();

                                        $( "#productTotals" ).html( products_total_html );
                                    } else {
                                    // jTable Mode
                                        productTotals.html( products_total_html )
                                            .find( "#tdb-update" )
                                            .button({ icons:{ primary:"ui-icon-disk" } })
                                            .addClass( "ui-priority-primary" ).parent()
                                            .removeClass( "tdbLink" );
                                        productTotals
                                            .find('#change_order_status')
                                            .multiselect({ buttonClass: 'btn btn-xs btn-info' });


                                        // Replace Orders total Product Quantity Field
                                        var productsRows           = $( products_total_html ).closest( "#productsTable" ).find( "tr.dataTableRow" ),
                                        jtableDataRow              = $( "tr.jtable-data-row[data-record-key=" + orderID + "]" ),
                                        products_purchased         = 0;

                                        productsRows.each(function() {
                                            products_purchased += parseInt( $( this ).find( "span" )[1].attributes['data-pred'].value, 10 );
                                        });
                                        jtableDataRow.find( "td.products_purchased" ).html( products_purchased );


                                        // Replace Order Total Value
                                        var order_total       = jtableDataRow.eq(0).find( "td.order_total" )[0],
                                        newPrice              = $( products_total_html ).find( "#ot_total_value" )[0].innerHTML;

                                        order_total.innerHTML = "<strong>" + newPrice + "</strong>";
                                    }
                                } else if ( payload.Result == "ERROR" ) {
                                    oHandler.gritter( "Error", payload.Message, "fa-thumbs-o-down" );
                                }
                            });
                            return false;
                        }
                    } );

                    return false;

                } else if ( action == "update" ) {
                    url = "get_table.php?action=update_product&pID=" + productID + "&oID=" + orderID + "&field=" + field + cID;
                    if ( extra !== "" ) {
                        url += "&extra=" + extra;
                    }
                    //url += "&new_value=";

                    if ( field == "options" ) {
                        return ( oHandler.tDialog( { url: url, orderID: orderID, pred: pred, extended: true, refreshTotals: true, capitalize: this } ) );

                    } else {
                        return ( oHandler.tDialog( { url: url, orderID: orderID, pred: pred, extended: false, refreshTotals: true, capitalize: this } ) );
                    }
                }
                return false;
            } );

            // Update Order Status With Comments
            $( "body" ).on( "click", "#orderTable button.ajax_button", function( event ) {
                event.preventDefault();

                var url = $( this ).closest( "#update_status" ).attr( "action" );
                if ( true === oHandler.configuration.settings.PHPMailer )
                    url += "&php_mailer=1";


                var jqxhr = $.ajax( {
                    type: "POST",
                    //global: false,
                    beforeSend: function( xhr ) {
                        settings = {
                            message: jTableOptions.loadingMessage,
                        };
                        if ( false === edit_order ) oHandler.showBusy.setup( settings );
                    },
                    url: url,
                    data: $( this ).closest( "#update_status" ).serialize(),
                    dataType: "json"
                }),
                orderID       =
                $( this )
                    .closest( "tr.jtable-child-row" )
                    .prev().attr( "data-record-key" ),
                jtableDataRow = ( true === edit_order ? $( "#update_status" ) :
                $( "tr.jtable-data-row[data-record-key=" + orderID + "]" )
                    .eq( 0 ).next() );

                jqxhr.done( function( payload ) {
                    if ( false === edit_order ) oHandler.showBusy.hide();
                    if ( payload.Result == "OK" ) {
                        jtableDataRow.find( "#comment_table" ).html( $.parseJSON( payload.comments ) );
                        oHandler.gritter( "Success", payload.Message, "fa-thumbs-o-up" );
                    } else if ( payload.Result == "ERROR" ) {
                        oHandler.gritter( "Warning", payload.Message, "fa-thumbs-o-down" );
                    }
                } );
                return false;
            });

            // Back button on 'Edit Order' Page
            $( "body" ).on( "click", "#pageHeading a.ajax_button", function( event ) {
                event.preventDefault();
                if ( document.location.href.replace( "&new_win=1", "" ) == document.referrer )
                    return window.open("get_table.php", '_self');
                return window.history.back();
            } );


            // Step Forward/Back Order Button on 'Edit Order' Page
            $( "body" ).on( "click", "#pageHeading span.ajax_button, #orderTable td.jtable-column-header", function( event ) {
                event.preventDefault();

                var link = $( this ).attr( "data-href" );
                if ( typeof link == "undefined" ) link = history.location.origin + history.location.pathname;

                return oHandler.ajaxLink( link, "orderTable" );
            } );

            // Add New Product
            $( "body" ).on( "click", "#addProduct", function( event ) {
                event.preventDefault();
                // Prevent Openining of more than  one Dialog
                if ( 1 < $( ".addProduct" ).length ) return false;
                var orderID = ( edit_order === true ) ? $( "#order_number" ).text() : $( this ).closest( "tr.jtable-child-row" ).prev().attr( "data-record-key" ),
                addProduct = $( "#add-Product" ).clone().insertAfter( this ),
                offset      = $( this ).offset();

                var cache = {};
                $( addProduct ).find( "#keywords" ).autocomplete({
                    minLength: 1,
                    appendTo: $( addProduct ).find( "#addProductFind" ),
                    select: function(event, ui) {
                        window.location  = (ui.item.id);
                    },
                    source: function( request, response ) {
                        var term = request.term;
                        if ( term in cache ) {
                            response( cache[ term ] );
                            return;
                        }

                        $.ajax({
                            dataType: "json",
                            url: "get_table.php?action=search_product",
                            data: request,
                            //global: false
                        })
                        .done(function( json ) {
                            cache[ term ] = json;
                            response( json );
                        });
                    }
                }).data("ui-autocomplete")._renderItem = function( ul, item ) {
                    return $( "<li></li>" )
                    .data( "item.autocomplete", item )
                    .append( '<a href="#" ' + item.id + '>'+ item.value + "</a>" )
                    .appendTo( ul )
                    .click( function( event ) {
                        event.preventDefault();
                        $( addProduct ).find( "#keywords" ).autocomplete( "close" );
                        var orderID = ( true === edit_order ? $( "#order_number" ).text() :
                        $( this )
                            .closest( "tr.jtable-child-row" ).prev()[0]
                            .attributes['data-record-key'].value );
                        oHandler.selectProduct( orderID, this.lastChild.attributes['data-pid'].value, this.lastChild.attributes['data-pname'].value );
                        return false;
                    });
                };

                addProduct
                    //.css( "top", offset.top )
                    .css( "left", offset.left )
                    .css( "display", "block" )
                    .find( "#tdbCancel" ).click(function() {
                        // s.hideAddProducts();
                        $( this ).closest( "#add-Product" ).remove();
                    });

                $( addProduct ).find( "#keywords" ).focus();

            } );

            // Toolbar Buttons
            $( "body" ).on( "click", "#orderTable a.edit_order, #ordersTable a:not(a.ajax_disable), #orderTable .splitPageLink, #orderTable a.tooltip_set, #orderTable tr.jtable-data-row > td:not(td.jtable-selecting-column), #pageHeading a.duplicate_order", function( event ) {

                event.preventDefault();
                var classes = $( this ).attr( "class" ),
                jqxhr       = "",
                ordersTable = "",
                data_link   = "";

                if ( typeof classes != "undefined" ) {
                    if ( classes.indexOf( "ajax_disable" ) >= 0 ) {
                        return true;
                    } else if ( classes.indexOf( "duplicate_order" ) >= 0 || classes.indexOf( "mail_confirmation" ) >= 0 ) {

                        var url = this.href;
                        if ( true === oHandler.configuration.settings.PHPMailer )
                            url += "&php_mailer=1";

                        jqxhr = $.ajax( {
                            type: "GET",
                            beforeSend: function( xhr ) {
                                settings = {
                                    message: jTableOptions.loadingMessage,
                                };
                                if ( false === edit_order ) oHandler.showBusy.setup( settings );
                            },
                            //global: false,
                            url: url,
                            dataType: "json"
                        } );

                        orderTable = $( "#orderTable" ).size();

                        jqxhr.done( function( data ) {
                            if ( classes.indexOf( "mail_confirmation" ) >= 0 ) {
                                if ( data.Result == "OK" ) {
                                    oHandler.gritter( "Success", data.Message, "fa-envelope" );
                                } else if ( data.Result == "ERROR" ) {
                                    oHandler.gritter( "Error", data.Message, "fa-thumbs-o-down" );
                                }
                                return false;
                            }

                            if ( data.Result == "OK" ) {
                                if ( false === edit_order && orderTable > 0 ) {
                                    $( "#orderTable" ).jtable( "addRecord", {
                                        index: 0,
                                        clientOnly: true,
                                        record: data.Records[ 0 ]
                                    } );
                                    if ( data.Records[ 0 ].orders_id > last_order_number )
                                        last_order_number = data.Records[ 0 ].orders_id;

                                    // Activate Bootstrap Multiselect on new Row
                                    var new_row = $( "#jTable" ).find( "tr.jtable-data-row" ).eq(0);
                                    new_row.find( "select" ).multiselect({ buttonClass: 'btn btn-xs btn-info' });
                                }
                                oHandler.gritter( "Order Duplicated", data.Message, "fa-copy" );
                            } else if ( data.status == "ERROR" ) {
                                oHandler.gritter( "Error", data.Message, "fa-thumbs-o-down" );
                            }
                            return false;
                        } );

                        jqxhr.always( function() {
                            if ( false === edit_order ) oHandler.showBusy.hide();
                        } );

                        return false;
                    } else if ( classes.indexOf( "edit_order" ) >= 0 ) {
                        // Get URL
                        data_link = this.href;

                        // Send to New Tab/Window if set in configuration
                        if ( true === oHandler.configuration.settings.newWin ) {
                            window.open( data_link + "&new_win=1", '_newedit' );
                            return false;
                        }


                        // Replace URL
                        history.pushState( null, null, data_link );

                        polling = false;
                        $.ajax( {
                            type: "GET",
                            url: encodeURI( data_link ),
                            async: true,
                            global: true,
                            dataType: "html"
                        } )
                        .done( function( data ) {
                            var replacement = $( data ).closest( "#orderTable" ),
                            pageHeading     = $( data ).closest( "#pageHeading" );
                            edit_order = true;
                            //$( replacement ).find( "span.comments, .tooltip_set" ).tooltip( "toggle" ).tooltip( "hide" );
                            $( "#pageHeading" ).replaceWith( pageHeading );
                            $( "#orderTable" ).replaceWith( replacement );

                        } );


                        return false;
                    } else if ( classes.indexOf( "search_customer_orders" ) >= 0 ) {
                        var cID = $( this ).attr( "data-search" );
                        url = document.location.origin + document.location.pathname;

                        if ( $( "#tdialog" ).length > 0 )
                            $.fn.closeTdialog();

                        if ( true === polling )
                            oHandler.messagesLongPolling.toggle( false, false );

                        if ( true === edit_order ) {
                            $( "#orderTable" ).html( "" );
                            history.pushState( null, null, url );
                            edit_order = false;
                            $( "#navigationBottom" ).show().animate( { bottom: 0 } );
                            $( "#pageHeading" ).find( ".pageHeading" ).remove();
                            oHandler.init( { search: cID } );
                            return;
                        } else {
                            $( "#orderTable" ).jtable( "load", { search: cID } );
                        }
                        return false;
                    }
                }


                var table_row = $( this ).closest( "tr" ),
                    table_id = table_row.attr( "id" ),
                    pos = $( "#orderTable" ).jtable( "isChildRowOpen", $( this ).closest( "tr" ) ),
                    filename = "";

                if ( pos === false ) {
                    if ( table_row.hasClass( "jtable-row-selected" ) ) {
                        table_row.removeClass( "jtable-row-selected" ).find( "input" ).prop( "checked", false );
                    } else {
                        // If Shift Key was pressed - then select all rows in between the two selections
                        if ( event.shiftKey && $( "#jTable" ).find( "tr.jtable-row-selected" ).length > 0 ) {
                            // Find current and first selected rows
                            var beforeRow = $( "#jTable" ).find( "tr.jtable-row-selected" ).eq(0),
                            beforeIndex   = $( "tr.jtable-data-row", $( "#jTable" ).closest( "table" ) ).index( beforeRow ),
                            currentIndex  = $( "tr.jtable-data-row", $( "#jTable" ).closest( "table" ) ).index( table_row );

                            if (beforeIndex > 0 && beforeIndex < currentIndex) {
                                $( "#jTable" ).find( "tr.jtable-data-row" )
                                    .slice( beforeIndex, currentIndex + 1 )
                                    .addClass( "jtable-row-selected" )
                                    .find( "input" ).prop( "checked", true );
                            } else {
                                $("#jTable").find( "tr.jtable-data-row" )
                                    .slice( currentIndex, beforeIndex + 1 )
                                    .addClass( "jtable-row-selected" )
                                    .find( "input" ).prop( "checked", true );
                            }
                        } else {
                            table_row.addClass( "jtable-row-selected" ).find( "input" ).prop( "checked", true );
                        }
                    }
                }
                return false;
            } );

            // Expand Table to Maximum Size
            s.buttons.expandTable.on( "click", function( event ) {
                $( "#square-checkbox-3" ).iCheck( "toggle" );
            });

            // Top Navbar Logout Button
            s.buttons.logoutButton.on( "click", function( event ) {
                document.location = $( "#logout-button" ).find( "a" )[0].href;
            });

            s.buttons.refreshButton.on( "click", function( event ) {
                $( "#orderTable" ).jtable('reload');
            });

            $( "body" ).on( "click", "td.select_me, span.select_me", function( event ) {
                selectText( event.target );
            });

            // Bottom Navbar Submit Button
            s.buttons.submitButton.on( "click", function( event ) {
                event.preventDefault();
                var url          = this.href,
                contentText      = $( "#contentText" ),
                nav              = $( "#navigationBottom" ),
                orderTable       = $( "#orderTable" ),
                notify           = nav.find( "input[name=notify]" ),
                autoupdate       = $( "#autoupdatestatus" ).find( "input" ),
                autoupdatestatus = autoupdate.parent().children( "input:checked" ).val(),
                target           = $( "#target_file" ).find( "input:checked" ).val(),
                statusName       = autostatus[ autostatus.value - 1 ].text,
                selectedRows     = orderTable.jtable( "selectedRows" ),
                settings         =
                {
                    message: "Orders Updated: {0} out of {1}",
                    progress: 0,
                    total: selectedRows.length
                };

                if ( true === oHandler.configuration.settings.PHPMailer )
                    url += "&php_mailer=1";

                if ( true === notify[0].checked || "Yes" == autoupdatestatus ) {
                    //$( "#loader_content" ).find( "div.progress" ).show();

                    jqxhr = $.ajax({
                        xhr: function() {
                            var xhr = new window.XMLHttpRequest();
                            oHandler.showBusy.setup( settings );

                            xhr.addEventListener( "progress", function( event ) {

                                oHandler.showBusy.update( event.loaded );
                                // if ( event.lengthComputable )
                                // {   //event.loaded the bytes browser receive
                                //     //event.total the total bytes seted by the header
                                //     //
                                //     //var percentComplete = (event.loaded / event.total)*100;
                                //     oHandler.showBusy.update( event.loaded );

                                // }

                            }, false);

                            return xhr;
                        },
                        type: 'POST',
                        cache: false,
                        url: url,
                        data: $( "#navbar" ).serialize() + "&" + $( "#batch_orders" ).serialize()
                    });

                    jqxhr.done(function( payload ) {
                        // Reset Navbar Selections
                        notify.eq(1).click();
                        autoupdate.eq(1).click();
                        $( "#target_file" ).find( "input" ).eq(0).click();

                        // Hide Progress div
                        //$( "#loader_content" ).find( "div.progress" ).hide();
                        if( 'number' === typeof payload ) {
                            var countOrdersUpdated = payload.toString().length;
                            oHandler.gritter( "Success", countOrdersUpdated + " Orders Updated.", "fa-repeat fa-spin" );

                            if ( true === oHandler.config.clearSelection ) {
                                // Remove Order form Order Table
                                //$('#orderTable').jtable('deleteRows', selectedRows );
                                selectedRows.each(function () {
                                    var record = $(this).data('record');
                                    orderTable.jtable( "deleteRecord", {
                                        clientOnly: true,
                                        key: record.orders_id
                                    });
                                });
                            } else {
                                // Replace Order Status
                                selectedRows.each(function( event ) {
                                    $( this ).removeClass( "jtable-row-selected" );
                                    var record = $(this).data( "record" );
                                    orderTable.jtable( "updateRecord", {
                                        clientOnly: true,
                                        record: {
                                            orders_id: record.orders_id,
                                            orders_status_name: statusName
                                        }
                                    });
                                });
                            }

                        } else if( payload.Result == 'OK' ) {
                            oHandler.gritter( "Success", payload.Message, "fa-repeat fa-spin" );
                        }
                        return false;
                    });

                } else {
                    if ( target != "invoice" && "envelope" != target )
                        s.buttons.batchOrders[0].target = "_self";
                    else if ( "envelope" == target )

                        /* Change This to automatically open the PDF
                         * in Preview/Acrobat/PDF Viewer.
                         * In Firefox, go to Firefox > Preferences > Applications
                         * Search for PDF and then select your software from the
                         * Drop-Down Menu.
                         */
                        window.open( "", "_self" ); // Use This if you use an external PDF Software
                        //window.open( "", "_envelope" ); // Use This if you open PDF in your Browser

                    else
                        window.open( "", "_newtab" );

                    s.buttons.batchOrders.submit();
                }

                return false;
            });

            s.buttons.gritterRemove.on( "click", function() {
                $.gritter.removeAll();
                $( "#gritterRemove" ).animate( { opacity: 0 }, 400 );
            });

            s.buttons.batchDelete.on( "click", function( event ) {
                event.preventDefault();

                $( "#batch_confirm" ).show();
                oHandler.ajaxOrderDelete();
            });

            s.buttons.batchConfirm.on( "click", function( event ) {
                event.preventDefault();

                $( "#myModal" ).modal( "hide" );
                // $( "#ajaxOrderDelete" ).hide();
                // $( "#batch_confirm" ).hide();
                // $( "#restock" ).hide();

                oHandler.ajaxDelete();
            });

            s.buttons.closeWindow.on( "click", function( event ) {
                event.preventDefault();
                $( "#ajax_loader" ).hide();
                return( $( "#ajax_cart_dialog" ).dialog( "close" ) );
            });

            s.buttons.childTable2.on( "click", function( event ) {
                var orders_id = this.id;
                $('#orderContainer').jtable('openChildTable',
                    $( this ).closest('tr'),
                    {
                        title: "Order",
                        actions: {
                            listAction: 'get_table.php?action=get_order&oID=' + orders_id
                        },
                        fields: {
                            name: {
                                title: 'Namn',
                                width: '10%'
                            },
                            street_address: {
                                title: 'street_address',
                                width: '10%'
                            },
                            city: {
                                title: 'city',
                                width: '10%'
                            },
                            postcode: {
                                title: 'postcode',
                                width: '10%'
                            },
                            country: {
                                title: 'country',
                                width: '10%'
                            },
                            telephone: {
                                title: 'telephone',
                                width: '10%'
                            }
                        }
                    });
                data.childTable.jtable('load');
            });

            // Submit Handlers

            s.buttons.selectStatus.submit(function( event ) {
                event.preventDefault();

                return $( "#orderTable" ).jtable('load');
            });

            s.buttons.batchOrders.on( "submit", function( event ){
                event.preventDefault();

                var navbar = $( "#navbar" ).clone().appendTo( "body" ),
                form = $( this ).find( "input" ).clone().appendTo( navbar ),
                target = navbar.find($("input[type='radio'][name='target_file']:checked")).val();

                if ( target != "invoice" && "envelope" != target )
                    navbar[0].target = "_self";
                else if ( "envelope" == target )
                    navbar[0].target = "_envelope";

                navbar
                    .submit()
                    .remove();
                return false;
            });

            // Change Handlers

            $( "body" ).on( "change", '.orders_status', function( event ) {
                event.preventDefault();

                var batch_order_numbers = {},
                    orderID = ( true === edit_order ) ? $( "#order_number" ).text() : $( this ).closest( "tr" ).attr( "data-record-key" );
                batch_order_numbers[ orderID ] = 'no';

                var data = {
                    batch_order_numbers: batch_order_numbers,
                    autostatus: this.value,
                    autoupdatestatus: 'Yes',
                    notify: 'No',
                };
                // return false;

                var jqxhr =
                    $.ajax({
                        type: 'POST',
                        url: encodeURI( $( "#navbar" ).find( "a.submit_button" )[0].href ),
                        data: data,
                        async: true,
                        dataType: 'json'
                    });

                jqxhr.done(function( payload ) {
                    oHandler.gritter( "Order Status Updated", "", "fa-repeat fa-spin" );
                });

            });

            $( "body" ).on( "change", '#shipping_method_pull_down, select.shipping_method_pull_down', function( event ) {
                event.preventDefault();

                var orderID = ( true === edit_order ) ? $( "#order_number" ).text() : $( this ).closest( "tr" ).attr( "data-record-key" ),
                settings    = {
                    orderID: orderID,
                    url: "get_table.php?action=update_shipping_method&oID=" + orderID,
                    newValue: this.value,
                    refreshTotals: true,
                };

                $.extend( settings, {
                    url_title: settings.url + "&new_value=" + this.value,
                } );

                return ( oHandler.updateShippingMethod( event, settings ) );
            });

            $( "body" ).on( "change", '#payment_method_pull_down, select.payment_method_pull_down', function( event ) {
                event.preventDefault();

                var orderID = ( true === edit_order ) ? $( "#order_number" ).text() : $( this ).closest( "tr" ).attr( "data-record-key" ),
                table       = $( this ).attr( "data-table" ),
                field       = $( this ).attr( "data-field" ),
                newValue    = JSON.parse( this.value ),
                settings    = {
                    orderID: orderID,
                    url: "get_table.php?action=update_order_field" + ( true === edit_order ? "&edit_order=1" : "" ) + "&oID=" + orderID + "&db_table=" + table + "&field=" + field,
                    newValue: newValue[ 1 ],
                };


                if ( "_manual" == newValue[ 0 ] ) {
                    var tdialog =
                    $.fn.tdialog( {
                        type: "prompt",
                        content: "Enter new Payment Method?",
                        title: "Payment Method",
                        icon: "confirm",
                        showOverlay: oHandler.configuration.settings.showOverlay,
                        effect: 'css3',
                        css3EffectIn: '',
                        css3EffectOut: 'bench-out',
                        promptCallback: function() {
                            newValue = $( "#newValue" );

                            $.extend( settings, {
                                newValue: $( "#newValue" ).val(),
                                url_title: settings.url + "&new_value=" + newValue.val(),
                            } );

                            return ( oHandler.updatePaymentMethod( event, settings ) );
                        }
                    } );

                    $.when( tdialog ).done( function() {
                        $( "#newValue" ).val( newValue[ 1 ] ).trigger( "focus" );
                    } );

                    return false;
                }

                $.extend( settings, {
                    url_title: settings.url + "&new_value=" + newValue[ 1 ],
                } );

                return ( oHandler.updatePaymentMethod( event, settings ) );
            });

            var elem = $( "#navigationTop" ).find( "input.js-switch" )[0];
            var init = new Switchery( elem );
            s.buttons.jsSwitch.on( "change", function() {
                $( "#adminAppMenu" ).toggleClass( "toggleMenu" );
                $( "#contentText" ).toggleClass( "toggleMenu" );
            });

            // Event Handlers

            // Select input field contents
            $( "body" ).on( "focus", "#newValue", function( event ) {
                this.select();
            } );

            $( "body" ).on( "keyup", function( event ) {
                if ( event.keyCode == 27 && 0 < $( "#tdialog" ).length ) {
                    if ( $( "#tdialog" ).length > 0 )
                        $.fn.closeTdialog();
                }
            });

            s.buttons.tax_value.multiselect({
                buttonClass: 'btn btn-default dialog_tax'
            });

            s.buttons.autostatus.multiselect({
                buttonClass: 'btn btn-lg btn-default'
            });

            $( "#envelope_back" ).multiselect({
                buttonClass: 'btn btn-lg btn-default',
                maxHeight: 400,
                onChange: function(element, checked) {
                    this.$select.find( "option" ).removeAttr( "selected" );
                    element.attr( "selected", "selected" );

                    // Toggle State of Tooltips
                    localStorage.evelopeSize = element[0].value;
                    this.$select.multiselect('select', localStorage.evelopeSize);
                }
            });

            s.buttons.status.select2().on( "select2-selecting", function( event ) {
                $( "#status option" ).eq( 0 ).attr( "selected", "selected" );
                $( "#select2-chosen-1" ).text( "All Orders" );
                if ( $( "#tdialog" ).length > 0 )
                    $.fn.closeTdialog();

                var form     = this.id,
                selected = event.val,
                param    = {},
                url      = document.location.origin + document.location.pathname;

                param[ form ] = selected;
                if ( "1" == selected ) {
                    polling = true;
                } else if ( true === polling ) {
                    oHandler.messagesLongPolling.toggle( event, false );
                }

                if ( true === edit_order ) {
                    $( "#orderTable" ).html( "" );
                    history.pushState( null, null, url );
                    edit_order = false;
                    $( "#navigationBottom" ).show().animate( { bottom: 0 } );
                    $( "#pageHeading" ).find( "td.pageHeading" ).remove();
                    oHandler.init( param );
                    return;
                } else {
                    $( "#orderTable" ).jtable( "load", param );
                }

            });

            s.buttons.searchOrders.select2({
                placeholder: "Search for an order",
                minimumInputLength: 3,
                quietMillis: 3000,
                ajax: { // instead of writing the function to execute the request we use Select2's convenient helper
                    url: "get_table.php?action=search_orders",
                    dataType: 'json',
                    data: function (term, page) {
                        return {
                            q: term, // search term
                            page_limit: 10
                        };
                    },
                    results: function (data, page) { // parse the results into the format expected by Select2.
                        // since we are using custom formatting functions we do not need to alter remote JSON data
                        return {results: data};
                    }
                },
                    dropdownCssClass: "bigdrop", // apply css that makes the dropdown taller
                    escapeMarkup: function (m) { return m; } // we do not want to escape markup since we are displaying html in results
            }).on("change", function( event ) {
                return( oHandler.ajaxLink( event.added.id, "orderTable" ) );
            });


            s.buttons.context_menu.each(function(){
                var self   = $(this),
                label      = self.next(),
                label_text = label.text();

                label.remove();
                self.iCheck({
                    checkboxClass: 'icheckbox_line-red',
                    radioClass: 'iradio_line-red',
                    insert: '<div class="icheck_line-icon"></div>' + label_text
                });
            });

            $( "body" ).on('ifChecked', '#context_menu', function( event ) {
                $( "div.icheckbox_line-red" ).addClass( "checked" );
                return oHandler.context_menu();
            }).on('ifUnchecked', '#context_menu', function( event ) {
                $( "div.icheckbox_line-red" ).removeClass( "checked" );
                return $( "#jTable" ).contextMenu( "destroy" );
            });

        },

        /* History - Go Back/Forward
        *************************************************************************/
        popstate: function() {
            $( window ).bind( 'popstate', function( event ) {

                // Receiving location from the window.history object
                var returnLocation = ( history.location.href.match( /\.php/ ) ? history.location.href : document.location.href );//.replace(/cID=\d+&{0,1}/g, '');

                $.ajax({
                    type: 'GET',
                    url: encodeURI( returnLocation ),
                    beforeSend: function ( xhr ) {
                        loading.fadeIn( 200 );
                    },
                    //global: false,
                    dataType: 'html'
                })
                .done(function( data ) {
                    if ( false === edit_order || 1 <= $( data ).filter( "#pageHeading" ).length ) { // Go to 'Edit Order' Mode
                        var replacement = $( data ).closest( '#orderTable' ),
                        pageHeading     = $( data ).closest( "#pageHeading" );
                        $( '#pageHeading' ).replaceWith( pageHeading );
                        $( '#orderTable' ).replaceWith( replacement );
                    } else { // Go to jTable Mode
                        $( '#batch_orders').replaceWith( data );
                        oHandler.context_menu();
                    }
                })
                .always(function() {
                    loading.fadeOut( 200 );
                });

                //return false;
            });
        },

        /* Ajax Setup
        *************************************************************************/
        ajaxSetup: function() {
            $(document)
            .ajaxSend(function( handler ) {
                start = Date.now();
                //start = performance.now();
            })
            .ajaxStart(function ( handler ) {
                if ( document.location.search.match(/edit_order/g, '') )// || ""  == document.location.search )
                    loading.fadeIn( 200 );
            })
            .ajaxStop(function ( handler ) {
                if ( document.location.search.match(/edit_order/g, '') )// || ""  == document.location.search )
                    loading.fadeOut( 200 );
            })
            .ajaxComplete(function( event, xhr, settings ) {
                if ( false === edit_order )
                    oHandler.showBusy.hide();
                else if ( true === edit_order )
                    loading.fadeOut( 200 );

                // Debug Page Load Time
                if ( "true" == localStorage.debugTime )// && 0 === settings.url.match( "action=get_orders" ).length )
                    oHandler.ajaxLogging( start, settings.url.split( "?" )[1], true );
            })
            .ajaxError(function( event, jqxhr, settings, exception ) {
                var message = "";
                if ( undefined !== jqxhr.responseText )
                    message = jqxhr.responseText;
                if ( undefined !== jqxhr.responseJSON )
                    message = jqxhr.responseJSON;
                oHandler.gritter( jqxhr.status + ": " + jqxhr.statusText, message, 'fa-warning' );
            });
        }

    },

    /* Get URL Params - Usage: $.urlParam('param_name');
    *************************************************************************/
    $.urlParam = function(name){
        var results = new RegExp('[?&]' + name + '=([^]*)').exec(window.location.href);

        if ( null === results )
           return null;
        else
           return results[1] || 0;
    },

    /* Save all input/select field values of element to cookie
    *************************************************************************/
    $.saveCookie = function( element, expireDays ){
        var str = "";
        element.find( "input, select" ).each(function(event){
            str += this.id + "=" + this.checked + "&";
        });
        if ( "" == str )
            str += element[0].id + "=" + element[0].checked + "&";

        str = str.slice(0, - 1);
        $.cookie( element[0].id, str, {
            expires: ( undefined !== expireDays ? expireDays : 30 ),
            secure: ( "https:" == window.location.protocol ? true : false ),
        });
    },

    /* Restore all input/select field values to element from cookie
    *************************************************************************/
    $.restoreCookie = function( element ){
        element.each(function(i){
            // Test for Cookie
            if ( undefined === $.cookie( element[i].id ) )
                return false;
            var str = $.cookie( element[i].id ),
            data    = str.split('&');
            $(data).each(function(element) {
                var a = this.split('=');
                // If true - assume checkbox
                if ( "true" == a[1] ) {
                    if ( $('#' + a[0])[0].className.toLowerCase().indexOf( "icheck" ) >= 0 )
                        $('#' + a[0]).iCheck( "check" );
                    else
                        $('#' + a[0])[0].checked = true;
                // If false - assume checkbox
                } else if ( "false" == a[1] ) {
                    if ( "iCheck-helper" == $('#' + a[0]).next()[0].className || "iCheck" == $('#' + a[0])[0].className )
                        $('#' + a[0]).iCheck( "uncheck" );
                    else
                        $('#' + a[0])[0].checked = false;
                // If not boolean - assume text
                } else {
                    $('#' + a[0]).val(decodeURIComponent( a[1] ));
                }
            });
        });
    },

    /* Remove Cookie and Reset Form
    *************************************************************************/
    $.resetForm = function( form ){
        form[0].reset();
        $.removeCookie(form[0].id);
        form.find( "input" ).iCheck('update');
    },

    /* Debug Function execution Time
    /* Usage: Execute debug_time() before and after debugged code
    *************************************************************************/
    debug_time = function( pure ) {
        if (typeof window.situation == "undefined")
            window.situation = true;
        else
            delete window.situation;
        if ( window.situation ) {
            window.start = new Date();
        } else {
            window.situation = false;

            var stop = new Date();

            var executionTime = stop - start;
            delete window.situation;

            if ( pure )
                return executionTime;
            return "Code executed in: " + executionTime + " ms";
        }
    },

    /* Log Input to Console
    *************************************************************************/
    log = function( message ) {
        console.log( message );
    },

    /* Select All in Element
    *************************************************************************/
    selectText = function( element ) {
        var doc = document,
        text = element,
        range,
        selection;

        if (doc.body.createTextRange) { //ms
            range = doc.body.createTextRange();
            range.moveToElementText(text);
            range.select();
        } else if (window.getSelection) { //all others
            selection = window.getSelection();
            range = doc.createRange();
            range.selectNodeContents(text);
            selection.removeAllRanges();
            selection.addRange(range);
        }
    };



    // public API
    return {
        config: config,
        init: init,
        showBusy: showBusy,
        configuration: configuration,
        statistics: statistics,
        contact: contact,
        ajaxOrderDelete: ajaxOrderDelete,
        ajaxDelete: ajaxDelete,
        ajaxLink: ajaxLink,
        capitalizeString: capitalizeString,
        formatPhone: formatPhone,
        tDialog: tDialog,
        tDialogRequest: tDialogRequest,
        json_message: json_message,
        updateShippingMethod: updateShippingMethod,
        updatePaymentMethod: updatePaymentMethod,
        ajaxLogging: ajaxLogging,
        gritter: gritter,
        context_menu: context_menu,
        selectProduct: selectProduct,
        hideAddProducts: hideAddProducts,
        setAttr: setAttr,
        scrollNav: scrollNav,
        initHeader: initHeader,
        // hideHeader: hideHeader,
        showTooltips: showTooltips,
        messagesLongPolling: messagesLongPolling,
        bindEventHandlers: bindEventHandlers,
        dialogForm: dialogForm
    };

})();

______________________________________________________

 

* End Part 6 *

______________________________________________________

Link to comment
Share on other sites

Replace ./admin/includes/modules/order_handler/ajax_update.php With This: (1/2 Part of this File)

<?php
/*
  Advanced Order Handler Rev3 for osCommerce 2.3.3
  Copyright (C) 2014  Jonas [email protected]

  This file is part of Advanced Order Handler Rev3.

  Advanced Order Handler Rev3 is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Advanced Order Handler Rev3 is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with Advanced Order Handler Rev3.  If not, see <http://www.gnu.org/licenses/>.
*/

class Orders_Ajax {

  #Define Tables to Variables
  public $tablename_ab       = TABLE_ADDRESS_BOOK;
  public $tablename_cs       = TABLE_CURRENCIES;
  public $tablename_c        = TABLE_CUSTOMERS;
  public $tablename_co       = TABLE_COUNTRIES;
  public $tablename_o        = TABLE_ORDERS;
  public $tablename_osh      = TABLE_ORDERS_STATUS_HISTORY;
  public $tablename_ot       = TABLE_ORDERS_TOTAL;
  public $tablename_p        = TABLE_PRODUCTS;
  public $tablename_a        = TABLE_PRODUCTS_ATTRIBUTES;
  public $tablename_ad       = TABLE_PRODUCTS_ATTRIBUTES_DOWNLOAD;
  public $tablename_op       = TABLE_ORDERS_PRODUCTS;
  public $tablename_opa      = TABLE_ORDERS_PRODUCTS_ATTRIBUTES;
  public $tablename_os       = TABLE_ORDERS_STATUS;
  public $tablename_pd       = TABLE_PRODUCTS_DESCRIPTION;
  public $tablename_po       = TABLE_PRODUCTS_OPTIONS;
  public $tablename_pov      = TABLE_PRODUCTS_OPTIONS_VALUES;
  public $tablename_opd      = TABLE_ORDERS_PRODUCTS_DOWNLOAD;
  public $tablename_s        = TABLE_SPECIALS;
  public $tablename_tc       = TABLE_TAX_CLASS;
  public $tablename_tr       = TABLE_TAX_RATES;
  public $tablename_z        = TABLE_ZONES;


  public function __construct( $link = 'db_link' ) {
    global $$link;
    $this->link = $$link;
  } # End Constructor

  public function ajax_logging( $POST ) {
    $parse_log = '';
    if ( function_exists('apc_exists') ) {
      if ( apc_exists( STORE_PAGE_PARSE_TIME_LOG ) ) {
        apc_read_cache( $parse_log, STORE_PAGE_PARSE_TIME_LOG );
      }
    }

    $parse_log .= strftime( STORE_PARSE_DATE_TIME_FORMAT ) . ' [' . $POST['time'] . '] ' . getenv( 'REQUEST_URI' ) . "\n";

    if ( function_exists('apc_store') )
      apc_store( STORE_PAGE_PARSE_TIME_LOG, $parse_log );

    error_log( strftime( STORE_PARSE_DATE_TIME_FORMAT ) . ' [' . $POST['time'] . '] ' . getenv( 'REQUEST_URI' ) . "\n", 3, STORE_PAGE_PARSE_TIME_LOG );

    #Send Success Message
    return json_encode( array( 'Result' => 'OK', 'Message' => $POST['time'] ) );
  }


  public function tep_db_update_totals( $order_id, $fix_shipping = false ) {
    global $cart, $currency, $currencies, $language, $order, $shipping_weight, $shipping_num_boxes, $total_count;

    $price = 0;
    $total = 0;
    $taxes = array();

    # Set Customers Country and Zone
    $order_query = mysqli_prepared_query( "SELECT currency, countries_id, delivery_address_format_id, delivery_country, countries_iso_code_2, countries_iso_code_3 FROM $this->tablename_o o JOIN $this->tablename_co c ON (c.countries_name = o.delivery_country) WHERE orders_id = ? LIMIT 1", "i", array( $order_id ) );

    include_once DIR_WS_CLASSES . 'order.php';
    $currency = $order_query[0]['currency'];
    $order = new order( $order_id );

    #Reset Order
    $order->info['tax']        = 0;
    $order->info['subtotal']   = 0;
    $order->info['total']      = 0;
    $order->info['tax_groups'] = array();

    $cart  = new shoppingCart();
    $order->content_type = $cart->get_content_type();

    $shopping_cart = mysqli_prepared_query( "SELECT * FROM $this->tablename_op WHERE orders_id = ?", "i", array( $order_id ) );

    foreach ( $shopping_cart as $key => $product ) {
      $cart->add_cart($product['products_id'], $product['products_quantity'], '', false);
    }

    $shipping_num_boxes = 1;
    $cart->weight = $cart->show_weight();
    $shipping_weight = $cart->show_weight();
    $total_count = $cart->count_contents();

    $order->delivery['country'] =
    array(
      'id'         => $order_query[0]['countries_id'],
      'title'      => $order_query[0]['delivery_country'],
      'iso_code_2' => $order_query[0]['countries_iso_code_2'],
      'iso_code_3' => $order_query[0]['countries_iso_code_3'],
    );
    $order->delivery['zone_id'] = $order_query[0]['delivery_address_format_id'];

    #Get the order's currency
    $currency             = mysqli_prepared_query( "SELECT currency, currency_value from $this->tablename_o WHERE orders_id = ?", "i", array( $order_id ) );

    # Add Any Existing Shipping Tax
    $order_shipping_query = mysqli_prepared_query( "SELECT title, value FROM $this->tablename_ot WHERE orders_id = ? AND class = 'ot_shipping'", "i", array( $order_id ) );

    # Continue if Shipping exists
    if ( count( $order_shipping_query ) ) {
      # Get all available Shipping Quotes
      $quote          = $this->get_shipping_modules( $order_id );
      $found_shipping = false;

      # Loop Through Quotes and Search for Shipping Title (First Attempt)
      for ( $i=0, $n=sizeof( $quote ); $i<$n; $i++ ) {
        $haystack = $quote[$i]->quotes['module'];
        $needle   = preg_replace( '/\s\(.+$/', '', $order_shipping_query[0]['title'] );

        if ( stristr(  $haystack, $needle ) ) {

          $found_shipping = true;
          $shipping_total =
          array(
            'id'          => $quote[$i]->quotes['id'],
            'title'       => ( $quote[$i]->quotes['module'] != '' ? $quote[$i]->quotes['module'] : $quote[$i]->quotes['methods'][0]['module'] ) . ' (' . $quote[$i]->quotes['methods'][0]['title'] . "):",
            'text'        => $currencies->format( $quote[$i]->quotes['methods'][0]['cost'] * ( $quote[$i]->quotes['tax'] / 100 + 1 ), true, $currency[0]['currency'], $currency[0]['currency_value'] ),
            'value'       => $quote[$i]->quotes['methods'][0]['cost'] * ( $quote[$i]->quotes['tax'] / 100 + 1 ),
            'tax'         => $quote[$i]->quotes['methods'][0]['cost'] * ( $quote[$i]->quotes['tax'] / 100 + 1 ) - $quote[$i]->quotes['methods'][0]['cost'],
            );

          $params = array( $shipping_total['title'], $shipping_total['text'], $shipping_total['value'], $order_id );

          # Update Shipping
          mysqli_prepared_query( "UPDATE $this->tablename_ot SET title = ?, text = ?, value = ? WHERE orders_id = ? AND class = 'ot_shipping'", "ssdi", $params );

          $shipping_modules = preg_replace( '/\s\(.*\)$/', '', $quote[$i]->quotes['module'] . ' (' . $quote[$i]->quotes['methods'][0]['title'] . ')' );


          # If shipping module was found - then
          # get Tax Description and Rate
          $tax_info = mysqli_prepared_query( "SELECT tax_rate, tax_description FROM $this->tablename_tr WHERE tax_class_id = ?", "i", array( $quote[$i]->tax_class ) );
          $taxes[] = array( 'tax_description' => $tax_info[0]['tax_description'], 'value' => $shipping_total['tax'] );
          break;
        }
      }

      if ( false === $found_shipping ) {
        # Loop Through Quotes and Search for Shipping Title (Second Attempt)
        for ( $i=0, $n=sizeof( $quote ); $i<$n; $i++ ) {

          $haystack = preg_replace( '/\d+/', '', $order_shipping_query[0]['title'] );
          $needle   = preg_replace( '/\d+/', '', $quote[$i]->quotes['methods'][0]['title'] );
          $extra    = ( isset( $quote[$i]->quotes['methods'][0]['module'] ) ? $quote[$i]->quotes['methods'][0]['module'] : $quote[$i]->title );

          if ( stristr(  $haystack, $needle ) || stristr( $haystack, $extra ) ) {

            $shipping_total =
            array(
              'id'          => $quote[$i]->quotes['id'],
              'title'       => ( $quote[$i]->quotes['module'] != '' ? $quote[$i]->quotes['module'] : $quote[$i]->quotes['methods'][0]['module'] ) . ' (' . $quote[$i]->quotes['methods'][0]['title'] . "):",
              'text'        => $currencies->format( $quote[$i]->quotes['methods'][0]['cost'] * ( $quote[$i]->quotes['tax'] / 100 + 1 ), true, $currency[0]['currency'], $currency[0]['currency_value'] ),
              'value'       => $quote[$i]->quotes['methods'][0]['cost'] * ( $quote[$i]->quotes['tax'] / 100 + 1 ),
              'tax'         => $quote[$i]->quotes['methods'][0]['cost'] * ( $quote[$i]->quotes['tax'] / 100 + 1 ) - $quote[$i]->quotes['methods'][0]['cost'],
              );

            $params = array( $shipping_total['title'], $shipping_total['text'], $shipping_total['value'], $order_id );

            # Update Shipping
            mysqli_prepared_query( "UPDATE $this->tablename_ot SET title = ?, text = ?, value = ? WHERE orders_id = ? AND class = 'ot_shipping'", "ssdi", $params );

            $shipping_modules = preg_replace( '/\s\(.*\)$/', '', $quote[$i]->quotes['module'] . ' (' . $quote[$i]->quotes['methods'][0]['title'] . ')' );


            # If shipping module was found - then
            # get Tax Description and Rate
            $tax_info = mysqli_prepared_query( "SELECT tax_rate, tax_description FROM $this->tablename_tr WHERE tax_class_id = ?", "i", array( $quote[$i]->tax_class ) );
            $taxes[] = array( 'tax_description' => $tax_info[0]['tax_description'], 'value' => $shipping_total['tax'] );
          }
        }
      }

      $order->info['tax']      += $shipping_total['tax'];
      $order->info['subtotal'] += $shipping_total['value'];
      $order->info['total']    += $shipping_total['value'];
    }

    # Configure some more GLOBAL Variables
    @$order->info['tax_groups'][ $tax_info[0]['tax_description'] ] += $shipping_total['tax'];
    $order->info['shipping_method'] = preg_replace( '/:$/', '', $shipping_total['title'] );
    $order->info['shipping_cost']   = $shipping_total['value'] - $shipping_total['tax'];

    $GLOBALS['shipping'] = array(
      'id'    => $shipping_total['id'] . '_' . $shipping_total['id'],
      'title' => $order->info['shipping_method'],
      'cost'  => $order->info['shipping_cost'],
    );


    # Load Order Total Modules
    $dir = getcwd();
    chdir( DIR_FS_DOCUMENT_ROOT );

    require_once DIR_WS_CLASSES . 'order_total.php';
    $order_total_modules = new order_total;

    chdir( $dir );

    # Try to determine Tax on Payment Method Cost
    $order_total_extra_modules = array();
    if ( defined( 'MODULE_ORDER_TOTAL_INSTALLED' ) && tep_not_null( MODULE_ORDER_TOTAL_INSTALLED ) ) {
      $modules = explode( ';', MODULE_ORDER_TOTAL_INSTALLED );

      reset( $modules );
      while ( list( , $value ) = each( $modules ) ) {
        include_once DIR_FS_CATALOG . DIR_WS_LANGUAGES . $language . '/modules/order_total/' . $value;
        include_once DIR_FS_CATALOG . DIR_WS_MODULES . 'order_total/' . $value;

        $class = substr( $value, 0, strrpos( $value, '.' ) );

        $module = new $class;
        $order_total_extra_modules[ $module->code ] = (int)$module->sort_order;
      }
    }

    # Get all available Payment Quotes
    $payment_module       = $this->get_payment_modules( $order->info['payment_method'] );
    $new_taxes            = array();

    foreach ( $order_total_extra_modules as $order_total => $tot ) {
      if (
           'ot_tax'      != $order_total
        && 'ot_shipping' != $order_total
        && 'ot_subtotal' != $order_total
        && 'ot_total'    != $order_total
        && false         === strpos( $order_total, 'ot_extra' )
      ) {
        $GLOBALS['payment'] = $payment_module['code'];
        include_once DIR_FS_CATALOG . DIR_WS_LANGUAGES . $language . '/modules/order_total/' . $order_total . '.php';
        include_once DIR_FS_CATALOG . DIR_WS_MODULES . 'order_total/' . $order_total . '.php';

        # Reset Tax Variables
        $order->info['tax']        = 0;
        $order->info['subtotal']   = 0;
        $order->info['total']      = 0;
        $order->info['tax_groups'] = '';


        $module = new $order_total;
        $module->process();

        $tax_updated = false;
        if ( '' == $order->info['tax_groups'] ) break;

        for ( $i=0; $i<sizeof( $taxes ); $i++ ) {

          if ( key( $order->info['tax_groups'] ) === $taxes[$i]['tax_description'] ) {
            $taxes[$i]['value'] += current( $order->info['tax_groups'] );
            $tax_updated = true;
          }
        }

        if ( false === $tax_updated ) {
          $taxes[] = array(
            'tax_description' => key( $order->info['tax_groups'] ),
            'value'           => current( $order->info['tax_groups'] ),
          );
        }
      }
    }


    # Select all Order Products
    $products_total_query = mysqli_prepared_query( "SELECT final_price, products_quantity, products_tax FROM $this->tablename_op WHERE orders_id = ?", "i", array( $order_id ) );

    if ( count( $products_total_query ) > 0 ) {
      foreach ( $products_total_query as $products_total ) {
        # Calculate Tax Multiplier
        $tax_percent = round( ( 100 + (float)$products_total['products_tax'] ) / 100, 4 );
        $price += (float)round( ( (float)$products_total['final_price'] * (int)$products_total['products_quantity'] ) * $tax_percent, 2 );
        # If Product have Tax - Then
        # Get Tax Description
        if ( tep_not_null( $products_total['products_tax'] ) && $products_total['products_tax'] > 0 ) {
          $tax_description = mysqli_prepared_query( "SELECT tax_description FROM $this->tablename_tr WHERE tax_rate = ?", "d", array( $products_total['products_tax'] ) );

          # Calculate and Add Taxes to Tax Array
          if ( sizeof( $taxes ) ) {

            $ya_esta = false;
            for ( $i=0; $i<sizeof( $taxes ); $i++ ) {
              if ( isset( $taxes[$i]['tax_description'] ) && $tax_description[0]['tax_description'] === $taxes[$i]['tax_description'] ) {
                $ya_esta = $i;
              }
            }

            if ( $ya_esta === false ) {
              $taxes[] = array( 'tax_description' => $tax_description[0]['tax_description'], 'value' => round( ( ( ( (float)$products_total['final_price'] * (int)$products_total['products_quantity'] ) * (float)$products_total['products_tax'] ) / 100 ), 4 ) );

            } else {
              $taxes[$ya_esta]['value'] += round( ( ( ( (float)$products_total['final_price'] * (int)$products_total['products_quantity'] ) * (float)$products_total['products_tax'] ) / 100 ), 4 );
            }
          } else {
            $taxes[] = array( 'tax_description' => $tax_description[0]['tax_description'], 'value' => round( ( ( ( (float)$products_total['final_price'] * (int)$products_total['products_quantity'] ) * (float)$products_total['products_tax'] ) / 100 ), 4 ) );
          }
        }
      }
    }

    # Time to go to hell ...
    $dir = getcwd();
    chdir( DIR_FS_DOCUMENT_ROOT );

    # load selected payment module
    require_once DIR_WS_CLASSES . 'payment.php';

    $payment = $this->get_payment_modules( $order->info['payment_method'] );
    $payment_modules = new payment( $payment['code'] );

    $include_modules = array('class' => $payment['code'], 'file' => $payment['code'] . '.php');

    include_once(DIR_WS_LANGUAGES . $language . '/modules/payment/' . $include_modules['file']);
    include_once(DIR_WS_MODULES . 'payment/' . $include_modules['file']);

    $payment_module = new $include_modules['class'];
    $order->info['payment_method']  = $payment_modules->selected_module;

    $shipping_total['sort_order'] = $GLOBALS['ot_shipping']->sort_order;
    $GLOBALS['ot_shipping']->output[] = $shipping_total;

    # Configure som additional Variables that might be necessary ..
    require_once DIR_WS_CLASSES . 'shipping.php';
    $shipping_modules = new shipping( array( "id" => $GLOBALS['shipping']['id'] ) );

    $order_totals = $order_total_modules->process();

    # ... and back again. What the hell happened?
    chdir( $dir );

    # Get Order Totals excluding Tax
    $orders_total_query = mysqli_prepared_query( "SELECT * FROM $this->tablename_ot WHERE orders_id = ? AND class != 'ot_tax' ORDER BY sort_order", "i", array( $order_id ) );

    $exisiting_order_totals = array();
    $new_order_total        = array();
    foreach ($orders_total_query as $key => $totals) {
      $exisiting_order_totals[ $totals['class'] ] = $totals['class'];
    }

    foreach ( $order_totals as $key => $totals ) {
      $new_order_total[ $totals['code'] ] = $totals['code'];
      if (
        ! in_array( $totals['code'], $exisiting_order_totals )
        && 'ot_tax'      != $totals['code']
        && 'ot_shipping' != $totals['code']
        && 'ot_subtotal' != $totals['code']
        && 'ot_total'    != $totals['code']
        && false         === strpos( $totals['code'], 'ot_extra' )
      ) {

        # Format Order Total and Update Database
        $new_text = $currencies->format( round( $totals['value'] ), true, $currency[0]['currency'], $currency[0]['currency_value'] );

        $params = array( $totals['title'], $new_text, (float)$totals['value'], $totals['code'], $order_id, $totals['sort_order'] );

        mysqli_prepared_query( "INSERT INTO $this->tablename_ot (title, text, value, class, orders_id, sort_order) VALUES (?, ?, ?, ?, ?, ?)", "ssdsii", $params );
      }
    }

    foreach ( $exisiting_order_totals as $key => $exist_total ) {
      if ( 'ot_tax'      != $exist_total
        && 'ot_shipping' != $exist_total
        && 'ot_subtotal' != $exist_total
        && 'ot_total'    != $exist_total
        && false         === strpos( $exist_total, 'ot_extra' )
      ) {
        if ( ! in_array( $exist_total, $new_order_total ) ) {
          mysqli_prepared_query( "DELETE FROM $this->tablename_ot WHERE orders_id = ? AND class = ?", "is", array( $order_id, $exisiting_order_totals[ $key ] ) );
        }
      }
    }

    # Get Order Totals excluding Tax
    $orders_total_query = mysqli_prepared_query( "SELECT * FROM $this->tablename_ot WHERE orders_id = ? AND class != 'ot_tax' ORDER BY sort_order", "i", array( $order_id ) );
    # Get 'Default Tax' for use with Totals we don't know Tax Rate of.
    $others_tax = tep_db_fetch_all( "SELECT tax_class_title, tax_rate FROM $this->tablename_tc tc LEFT JOIN $this->tablename_tr tr USING (tax_class_id) WHERE tax_class_id = 1" );

    foreach ( $orders_total_query as $order_total ) {

      if ( $order_total['class'] == 'ot_subtotal' ) {

        $new_value = (float)$price;
        $new_text = $currencies->format( $new_value, true, $currency[0]['currency'], $currency[0]['currency_value'] );

        $total += (float)$new_value;

        $params = array( $new_text, $new_value, $order_total['orders_total_id'] );
        mysqli_prepared_query( "UPDATE $this->tablename_ot SET text = ?, value = ? WHERE orders_total_id = ?", "sdi", $params );

      } elseif ( $order_total['class'] == 'ot_total' ) {

        # Format Order Total and Update Database
        $new_value = (float)$total;
        $new_text = '<strong>' . $currencies->format( round( $new_value ), true, $currency[0]['currency'], $currency[0]['currency_value'] ) . '</strong>';

        $params = array( $new_text, $new_value, $order_total['orders_total_id'] );
        mysqli_prepared_query( "UPDATE $this->tablename_ot SET text = ?, value = ? WHERE orders_total_id = ?", "sdi", $params );
      } elseif ( $order_total['class'] == 'ot_shipping' ) {
        # Add Shipping Cost to Order Total
        $total += round( (float)$order_total['value'], 4 );
      } else {

        #Do the Extra Order Fields
        $updated = false;
        $class = explode( ",", $order_total['class'] );

        # Add Tax if Set
        if ( ( ! isset( $class[1] ) || ( isset( $class[1] ) && '0' != $class[1] ) ) && strpos($class[0], 'ot_extra_') !== false ) {

          # Calculate Special Tax Class
          if ( 1 < count( $class ) ) {
            $others_tax_extra = tep_db_fetch_all( "SELECT tax_rate, tax_description FROM tax_rates tr WHERE tax_class_id = " . (int)$class[1] );
            $tax_rate  = $others_tax_extra[0]['tax_rate'] / 100 + 1;
            $other_tax = $order_total['value'] - ( $order_total['value'] / $tax_rate );
            $tax_class_title = $others_tax_extra[0]['tax_description'];

            # Calculate Standard Tax Class
          } else {
            $tax_rate  = $others_tax[0]['tax_rate'] / 100 + 1;
            $other_tax = $order_total['value'] - ( $order_total['value'] / $tax_rate );
            $tax_class_title = $others_tax[0]['tax_class_title'];
          }

          $new_value = $order_total['value'] + $other_tax;
          $new_text  = $currencies->format( $new_value, true, $currency[0]['currency'], $currency[0]['currency_value'] );


          # Add Calculated Tax
          for ( $i=0; $i<sizeof( $taxes ); $i++ ) {
            if ( $taxes[$i]['tax_description'] == $tax_class_title ) {
              $taxes[ $i ]['value'] += (float)$other_tax;
              $updated = true;
              break;
            }
          }
          # Add new Tax Class
          if ( false === $updated ) {
            $taxes[] = array( 'tax_description' => $tax_class_title, 'value' => $other_tax );
          }
        }

        # Add Tax to Order Total
        $total += round( (float)$order_total['value'], 4 );
      }
    }

    # The Taxes
    if ( sizeof( $taxes ) ) {
      $orders_total_tax_query = mysqli_prepared_query( "SELECT * FROM $this->tablename_ot WHERE orders_id = ? AND class = 'ot_tax'", "i", array( $order_id ) );

      # Update the ot_tax with the same title
      # If title doesn't exist, INSERT it
      $tax_updated = array();
      foreach ( $orders_total_tax_query as $orders_total_tax ) {
        $eliminate_tax = true;
        for ( $i=0; $i<sizeof( $taxes ); $i++ ) {
          if ( $orders_total_tax['title'] === $taxes[$i]['tax_description'] && !in_array($taxes[$i]['tax_description'], $tax_updated)) {
            $eliminate_tax = false;
            # Keep in variable that this tax is done
            $tax_updated[] = $orders_total_tax['title'];

            # Prepare text (value with currency)
            $new_text = $currencies->format( $taxes[$i]['value'], true, $currency[0]['currency'], $currency[0]['currency_value'] );

            $params = array( $new_text, $taxes[$i]['value'], $orders_total_tax['orders_total_id'] );

            mysqli_prepared_query( "UPDATE $this->tablename_ot SET text = ?, value = ? WHERE orders_total_id = ? AND class = 'ot_tax'", "sdi", $params );
          }
        }

        # Eliminate Tax Class
        if ( $eliminate_tax == true ) {
          mysqli_prepared_query( "DELETE FROM $this->tablename_ot WHERE orders_total_id = ? limit 1", "i", array( $orders_total_tax['orders_total_id'] ) );
        }
      }

      # INSERT a new tax rate in the orders_total table, if all of taxes[] is not in $tax_updated[]

      for ( $i=0; $i<sizeof( $taxes ); $i++ ) {
        if ( ( ! in_array( $taxes[$i]['tax_description'], $tax_updated ) ) && ( (float)$taxes[$i]['value'] > 0 ) ) {
          # prepare text (value with currency)
          $texto = round( (float)$taxes[$i]['value'], 2 );
          $texto = $currencies->format( $texto, true, $currency[0]['currency'], $currency[0]['currency_value'] );

          if ( null !== $taxes[$i]['tax_description'] )
            $params = array( $order_id, $taxes[$i]['tax_description'] . ':', $texto, $taxes[$i]['value'] );

          mysqli_prepared_query( "INSERT INTO $this->tablename_ot (orders_id, title, text, value, class, sort_order) VALUES (?, ?, ?, ?, 'ot_tax', 3)", "issd", $params );
        }
      }
    }

    # We're done if we only want to fix shipping
    if ( true === $fix_shipping ) return false;

    # Try to Fix Order Totals Sort Order
    $order_total_modules = array();
    if ( defined( 'MODULE_ORDER_TOTAL_INSTALLED' ) && tep_not_null( MODULE_ORDER_TOTAL_INSTALLED ) ) {
      $modules = explode( ';', MODULE_ORDER_TOTAL_INSTALLED );

      reset( $modules );
      while ( list( , $value ) = each( $modules ) ) {
        include_once DIR_FS_CATALOG . DIR_WS_LANGUAGES . $language . '/modules/order_total/' . $value;
        include_once DIR_FS_CATALOG . DIR_WS_MODULES . 'order_total/' . $value;

        $class = substr( $value, 0, strrpos( $value, '.' ) );
        $GLOBALS[$class] = new $class;
        $order_total_modules[ $GLOBALS[$class]->code ] = (int)$GLOBALS[$class]->sort_order;
      }
    }

    $sql  = "UPDATE $this->tablename_ot SET sort_order = ? WHERE orders_id = ? AND class = ?";

    $stmt = $this->link->prepare( $sql );

    /* Bind parameters. Types: s = string, i = integer, d = double,  b = blob */
    $stmt->bind_param( 'iis', $sort_order, $order_id, $class );

    foreach ( $order_total_modules as $class => $sort_order ) {
      /* Execute statement */
      $stmt->execute();
    }

    $stmt->close();

    #SQL
    $sort_extra_sort_order_sql = "SELECT (SELECT max(sort_order) + 1 FROM orders_total WHERE orders_id = o.orders_id AND class != 'ot_total') as maxim, (SELECT COUNT(class) FROM orders_total WHERE orders_id = o.orders_id AND class like '%ot_extra_%') as class FROM orders o JOIN orders_total USING (orders_id) WHERE orders_id = ? GROUP BY orders_id";
    $sort_extra_sort_order = mysqli_prepared_query( $sort_extra_sort_order_sql, "i", array( $order_id ) );

    # If Extra fields - Then Update Sort Order for Order Total
    if ( ! empty($sort_extra_sort_order) )
      mysqli_prepared_query( "UPDATE $this->tablename_ot SET sort_order = ? WHERE orders_id = ? AND class = 'ot_total'", "ii", array( $sort_extra_sort_order[0]['maxim'], $order_id ) );

    //$this->tep_db_update_totals( $order_id );

  } # End tep_db_update_totals

  # Return All available Shipping Modules
  public function get_shipping_modules( $order_id = null ) {
    global $language;//, $order, $cart;

    # Set Customers Country and Zone
    if ( null !== $order_id ) {
      $order_query = mysqli_prepared_query( "SELECT currency, countries_id, delivery_address_format_id, delivery_country, countries_iso_code_2, countries_iso_code_3 FROM $this->tablename_o o JOIN $this->tablename_co c ON (c.countries_name = o.delivery_country) WHERE orders_id = ? LIMIT 1", "i", array( $order_id ) );

      $order->delivery['country'] =
      array(
        'id'         => $order_query[0]['countries_id'],
        'title'      => $order_query[0]['delivery_country'],
        'iso_code_2' => $order_query[0]['countries_iso_code_2'],
        'iso_code_3' => $order_query[0]['countries_iso_code_3'],
      );
      $order->delivery['zone_id'] = $order_query[0]['delivery_address_format_id'];
      $currency = $order_query[0]['currency'];
    }

    if ( ! is_array( $order->delivery['country'] ) ) {
      # Get the Countries iso_code_2
      $sql_country            = "SELECT * FROM $this->tablename_co WHERE countries_name = ?";
      $country                = mysqli_prepared_query( $sql_country, "s", array( $order->delivery['country'] ));

      $order->delivery['country']     =
      array(
        'id'                            => $country[0]['countries_id'],
        'title'                         => $country[0]['countries_name'],
        'iso_code_2'                    => $country[0]['countries_iso_code_2'],
        'iso_code_3'                    => $country[0]['countries_iso_code_3']
      );
    }

    if ( defined( 'MODULE_SHIPPING_INSTALLED' ) && tep_not_null( MODULE_SHIPPING_INSTALLED ) ) {
      $this->modules = explode( ';', MODULE_SHIPPING_INSTALLED );

      $include_modules = array();

      reset( $this->modules );
      while ( list( , $value ) = each( $this->modules ) ) {
        $class = substr( $value, 0, strrpos( $value, '.' ) );
        $include_modules[] = array( 'class' => $class, 'file' => $value );
      }

      for ( $i=0, $n=sizeof( $include_modules ); $i<$n; $i++ ) {
        include_once DIR_FS_CATALOG . DIR_WS_LANGUAGES . $language . '/modules/shipping/' . $include_modules[$i]['file'];
        include_once DIR_FS_CATALOG . DIR_WS_MODULES . 'shipping/' . $include_modules[$i]['file'];

        $modules[$i] = new $include_modules[$i]['class'];
        $modules[$i]->quote();
      }
    }

    return $modules;
  } # END get_shipping_modules

  # Get Selected Payment Module based on Title
  public function get_payment_modules( $payment_method ) {
    global $language;

    if ( defined( 'MODULE_PAYMENT_INSTALLED' ) && tep_not_null( MODULE_PAYMENT_INSTALLED ) ) {
      $installed_modules       = explode( ';', MODULE_PAYMENT_INSTALLED );

      $include_modules         = array();
      $modules                 = array();
      $payment                 = array();

      reset( $modules );
      while ( list( , $value ) = each( $installed_modules ) ) {
        $class                 = substr( $value, 0, strrpos( $value, '.' ) );
        $include_modules[]     = array( 'class' => $class, 'file' => $value );
      }

      for ( $i=0, $n=sizeof( $include_modules ); $i<$n; $i++ ) {
        include_once DIR_FS_CATALOG . DIR_WS_LANGUAGES . $language . '/modules/payment/' . $include_modules[$i]['file'];
        include_once DIR_FS_CATALOG . DIR_WS_MODULES . 'payment/' . $include_modules[$i]['file'];

        $modules = new $include_modules[$i]['class'];

        if ( ( isset( $modules->moduleinfo['name'] ) && $payment_method == $modules->moduleinfo['name'] ) || $payment_method == $modules->title ) {
          $payment['code'] = ( isset( $modules->code ) ? $modules->code : null );
          $payment['email_footer'] = ( isset( $modules->email_footer ) ? $modules->email_footer : null );
          $payment['sort_order'] = ( isset( $modules->sort_order ) ? $modules->sort_order : null );
          break;
        }

      }
    }

    if ( ! isset( $payment['code'] ) )
      $payment['code'] = false;

    return $payment;
  } # END get_payment_modules

  # Search value from first array key in multi dimensional array and move it to the top.
  private function array_move_key_to_top( $array, $needle ) {
    foreach ( $array as $key => $val ) {                        # loop all elements
      if ( false !== strstr( $val[ key( $val ) ], $needle ) ) { # check for $needle in array key
        unset( $array[ $key ] );         # unset the $array with id $needle
        array_unshift( $array, $val );   # unshift the array with $val to push in the beginning of array
        return $array;
      }
    }
  }

  # Create JSON Array with Modules for jTable Dropdown
  public function get_modules( $module_type, $module_directory, $default = false ) {
    global $language;

    if ( empty( $module_type ) || empty( $module_directory ) )
      return false;

    $this->modules = explode( ';', $module_type );

    $include_modules = array();

    reset( $this->modules );
    while ( list( , $value ) = each( $this->modules ) ) {
      $class = substr( $value, 0, strrpos( $value, '.' ) );
      $include_modules[] = array( 'class' => $class, 'file' => $value );
    }

    for ( $i=0, $n=sizeof( $include_modules ); $i<$n; $i++ ) {
      include_once DIR_FS_CATALOG . DIR_WS_LANGUAGES . $language . '/modules/' . $module_directory . '/' . $include_modules[$i]['file'];
      include_once DIR_FS_CATALOG . DIR_WS_MODULES . $module_directory .'/' . $include_modules[$i]['file'];

      $modules[] = (array) new $include_modules[ $i ]['class'];
    }

    if ( false !== $default )
      $modules = $this->array_move_key_to_top( $modules, $default );

    $modules_array = '';
    foreach ( $modules as $value ) {
      $modules_array .=
        json_encode(
        array(
          'Value' => serialize( array( $value['code'], $value['title'] ) ),
          'DisplayText' => $value['title']
        )
      ) . ', ';
    }

    $modules_array = substr( $modules_array, 0, -2 );

    return $modules_array;
  }

  # Create jTable Dropdown with currencies
  public function get_currencies() {
    global $language, $currencies;

    $currencies_array = array();
    while ( list( $key, $value ) = each( $currencies->currencies ) ) {
      $currencies_array[] = array( 'id' => $key . "," . $value["value"], 'text' => $value['title'] );
    }

    $currencies_array = $this->array_move_key_to_top( $currencies_array, DEFAULT_CURRENCY );

    $currency_array = '';
    foreach ( $currencies_array as $currency ) {
      $currency_array .=
        json_encode(
        array(
          'Value' => $currency['id'],
          'DisplayText' => $currency['text']
        )
      ) . ', ';
    }

    $currency_array = substr( $currency_array, 0, -2 );

    return $currency_array;
  }

  public function update_orders_status( $GET, $POST ) {
    global $languages_id;
    header( 'Content-Type: application/json' );

    #PHPMailer Class
    if ( isset( $GET['php_mailer'] ) ) {
      require_once DIR_WS_MODULES . 'order_handler/PHPMailer/PHPMailerAutoload.php';
      $mail = new PHPMailer;

      $mail->isSMTP();                          # Set mailer to use SMTP
      $mail->Host       = PHPMAILER_HOST;       # Specify main and backup SMTP servers
      $mail->SMTPAuth   = PHPMAILER_SMTP_AUTH;  # Enable SMTP authentication
      $mail->Username   = PHPMAILER_USERNAME;   # SMTP username
      $mail->Password   = PHPMAILER_PASSWORD;   # SMTP password
      $mail->SMTPSecure = PHPMAILER_SECURE;     # Enable encryption, 'ssl' also accepted
      $mail->CharSet    = PHPMAILER_CHARSET;    # Character Encoding

      $mail->From       = PHPMAILER_FROM;       # Sender E-Mail address
      $mail->FromName   = PHPMAILER_FROMNAME;   # Sender Name
    }

    #Sort Orders to be Updated
    if ( $POST['batch_order_numbers'] ) {
      foreach ( $POST['batch_order_numbers'] as $order_number => $print_order ) {
        $batch_order_numbers[] = $order_number;
      }
    }

    sort( $batch_order_numbers );

    #Here we set the Progress Total Size
    $count_orders = count( $batch_order_numbers );
    header( "Content-Length: " . $count_orders );

    #Begin ERROR handling
    if ( !( is_array( $batch_order_numbers ) ) ) {
      #HTTP/1.1 404 Not Found
      http_response_code( 404 );
      die( json_encode( array( 'Result' => 'ERROR', 'Message' => ERROR_NO_ORDERS_SELECTED ) ) );
    }

    #Get the name of the Order Status
    $orders_statuses = array();
    $orders_status_array = array();
    $orders_status_query = mysqli_prepared_query( "SELECT orders_status_name from $this->tablename_os WHERE language_id = ? AND orders_status_id = ?", "ii", array( $languages_id, $POST['autostatus'] ) );
    $orders_status = $orders_status_query[0]['orders_status_name'];

    #Connect
    $link = "db_link";
    global $$link;

    #SQL
    $tablename_o    = TABLE_ORDERS;
    $tablename_osh  = TABLE_ORDERS_STATUS_HISTORY;
    $sql_se         = "SELECT customers_id, customers_name, customers_email_address, payment_method, date_purchased, orders_status, date_purchased FROM $tablename_o WHERE orders_id = ?";
    $sql_in         = "INSERT INTO $this->tablename_osh (orders_id, orders_status_id, date_added, customer_notified) VALUES (?, ?, now(), ?)";
    $sql_up         = "UPDATE $tablename_o SET orders_status = ? WHERE orders_id = ?";

    #Prepare Statement
    $stmt_se        = $$link->prepare( $sql_se );
    $stmt_in        = $$link->prepare( $sql_in );
    $stmt_up        = $$link->prepare( $sql_up );

    #Error Handling
    $error = false;

    if ( false === $stmt_se )
      $error = json_encode( array( 'Result' => 'ERROR', 'Message' => "Preparing '$sql_se' statement failed: " . htmlspecialchars( $this->link->error ) ) );
    if ( false === $stmt_in )
      $error = json_encode( array( 'Result' => 'ERROR', 'Message' => "Preparing '$sql_in' statement failed: " . htmlspecialchars( $this->link->error ) ) );
    if ( false === $stmt_up )
      $error = json_encode( array( 'Result' => 'ERROR', 'Message' => "Preparing '$sql_up' statement failed: " . htmlspecialchars( $this->link->error ) ) );

    if ( false !== $error ) {
      #HTTP/1.1 400 Bad Request
      http_response_code( 400 );
      die( $error );
    }

    #Bind parameters to the query
    $bp_se = $stmt_se->bind_param( "i", $order_number );
    $bp_in = $stmt_in->bind_param( "iii", $order_number, $POST['autostatus'], $customer_notified );
    $bp_up = $stmt_up->bind_param( "ii", $POST['autostatus'], $order_number );


    #Error checking for binded parameters
    if ( false === $bp_se )
      $error = json_encode( array( 'Result' => 'ERROR', 'Message' => "Binding parameters for '$sql_se' failed: " . htmlspecialchars( $this->link->error ) ) );
    if ( false === $bp_in )
      $error = json_encode( array( 'Result' => 'ERROR', 'Message' => "Binding parameters for '$sql_in' failed: " . htmlspecialchars( $this->link->error ) ) );
    if ( false === $bp_up )
      $error = json_encode( array( 'Result' => 'ERROR', 'Message' => "Binding parameters for '$sql_up' failed: " . htmlspecialchars( $this->link->error ) ) );

    if ( false !== $error ) {
      #HTTP/1.1 400 Bad Request
      http_response_code( 400 );
      die( $error );
    }


    #Let's iterate the array
    foreach ( $batch_order_numbers as $order_number ) {

      $check_status = array();
      if ( $POST['autoupdatestatus'] == 'Yes' && is_numeric( $POST['autostatus'] ) ) {

        #Run the prepared statement
        $bp_se = $stmt_se->execute();

        #Error handling
        if ( false === $bp_se ) {
          #HTTP/1.1 400 Bad Request
          http_response_code( 400 );
          die( json_encode( array( 'Result' => 'ERROR', 'Message' => 'Executing SELECT failed: ' . htmlspecialchars( $this->link->error ) ) ) );
        }

        # Get the customers info
        if ( true === function_exists( 'mysqli_stmt_get_result' ) ) {
          # With Mysqlnd
          $res = $stmt_se->get_result();
          while ( ( $row = $res->fetch_assoc() ) )
            $check_status[] = $row;
        } else {
          # Without Mysqlnd
          $check_status = mysqli_get_res( $stmt_se );
        }

        #Start, if selected, with E-Mail Status Update
        $customer_notified = '0';

        #Error Handling
        $error = false;

        if ( 'Yes' == $POST['notify'] ) {

          #PHPMailer Class
          if ( isset( $GET['php_mailer'] ) ) {
            $mail->addAddress( $check_status[0]['customers_email_address'], $check_status[0]['customers_name'] );          # Add a recipient
            $mail->WordWrap = PHPMAILER_WORDWRAP;
            $mail->ContentType = PHPMAILER_CONTENTTYPE;
            $mail->isHTML( PHPMAILER_IS_HTML ); # Set email format to HTML
          }

          $email_order = "\n" .
            STORE_NAME . "\n\n" .
            EMAIL_SEPARATOR . "\n" .
            EMAIL_TEXT_ORDER_NUMBER . ' ' . $order_number . "\n" .
            EMAIL_TEXT_INVOICE_URL . ' ' . tep_catalog_href_link( FILENAME_CATALOG_ACCOUNT_HISTORY_INFO, 'order_id=' . $order_number, 'SSL', false ) . "\n" .
            EMAIL_TEXT_DATE_ORDERED . ' ' . tep_date_long( $check_status[0]['date_purchased'] ) . "\n\n" . EMAIL_SEPARATOR . "\n" .
            sprintf( EMAIL_TEXT_STATUS_UPDATE, $orders_status ) . "\n";

          $email_subj = EMAIL_TEXT_ORDER_NUMBER .  ' ' . $order_number . " (Status: " . $orders_status .")";

          #PHPMailer Class
          if ( isset( $GET['php_mailer'] ) ) {
            $mail->Subject = $email_subj;
            $mail->Body       = nl2br( $email_order );
            $mail->AltBody    = strip_tags( $email_order );
          }


          if ( 'true' == SEND_EMAILS ) {
            #PHPMailer Class
            if ( isset( $GET['php_mailer'] ) ) {

              if ( ! $mail->send() ) {
                http_response_code( 400 );
                die( 'Mailer Error: ' . $mail->ErrorInfo );
              }
              $mail->clearAddresses();

            } else {
              #PHP mail()
              tep_mail( $check_status[0]['customers_name'], $check_status[0]['customers_email_address'], $email_subj, $email, STORE_OWNER, STORE_OWNER_EMAIL_ADDRESS );
            }
          }

          $customer_notified = '1';
        }

        #Run the prepared statements
        $bp_in = $stmt_in->execute();
        $bp_up = $stmt_up->execute();


        if ( false === $bp_in )
          $error = json_encode( array( 'Result' => 'ERROR', 'Message' => 'Executing INSERT failed: ' . htmlspecialchars( $this->link->error ) ) );
        if ( false === $bp_up )
          $error = json_encode( array( 'Result' => 'ERROR', 'Message' => 'Executing UPDATE failed: ' . htmlspecialchars( $this->link->error ) ) );

        if ( false !== $error ) {
          #HTTP/1.1 400 Bad Request
          http_response_code( 400 );
          die( $error );
        }

      }
      #Output Progress
      echo "1";
      ob_flush();
      flush();
    }

    ob_end_flush();

    #Close open connections
    $stmt_se->close();
    $stmt_in->close();
    $stmt_up->close();

    die;
  }

  public function delete_confirm( $POST ) {
    header( 'Content-Type: application/json' );

    $restock = false;

    if ( isset( $_POST['restock'] ) )
      $restock = true;

    #Here we set the Progress Total Size
    $count_orders = count( $POST['order_nums'] );
    header( "Content-Length: " . $count_orders );

    foreach ( $POST['order_nums'] as $oID ) {
      tep_remove_order( $oID, $restock );

      #Output Progress
      echo "1";
      ob_flush();
      flush();
    }

    ob_end_flush();

    die;
  }

  public function update_order( $GET, $POST ) {
    global $languages_id;
    header( 'Content-Type: application/json' );


    $order_updated    = false;
    $error            = false;
    $message          = '';
    $POST['comments'] = stripslashes( $POST['comments'] );

    $orders_statuses     = array();
    $orders_status_array = array();
    $orders_status_query = mysqli_prepared_query( "SELECT orders_status_id, orders_status_name FROM $this->tablename_os WHERE language_id = ?", "i", array( $languages_id ) );
    foreach ( $orders_status_query as $orders_status ) {
      $orders_statuses[] = array( 'id' => $orders_status['orders_status_id'],
        'text' => $orders_status['orders_status_name'] );
      $orders_status_array[$orders_status['orders_status_id']] = $orders_status['orders_status_name'];
    }

    $check_status_query = mysqli_prepared_query( "SELECT customers_id, customers_name, customers_email_address, payment_method, date_purchased, orders_status, date_purchased FROM $this->tablename_o WHERE orders_id = ?", "i", array( $GET['oID'] ) );
    $check_status      = $check_status_query[0];

    if ( ( $check_status['orders_status'] != $POST['status'] ) || tep_not_null( $POST['comments'] ) ) {
      mysqli_prepared_query( "UPDATE $this->tablename_o SET orders_status = ?, last_modified = now() WHERE orders_id = ?", "ii", array( $POST['status'], $GET['oID'] ) );

      $customer_notified = '0';
      if ( isset( $POST['notify'] ) && ( $POST['notify'] == 'on' ) ) {
        $notify_comments = '';
        if ( isset( $POST['notify_comments'] ) && ( $POST['notify_comments'] == 'on' ) ) {
          $notify_comments .= sprintf( EMAIL_TEXT_COMMENTS_UPDATE, $POST['comments'] ) . "\n\n";
        }

        $email = STORE_NAME . "\n" . EMAIL_SEPARATOR . "\n" . EMAIL_TEXT_ORDER_NUMBER . ' ' . $GET['oID'] . "\n" . EMAIL_TEXT_INVOICE_URL . ' ' . tep_catalog_href_link( FILENAME_CATALOG_ACCOUNT_HISTORY_INFO, 'order_id=' . $GET['oID'], 'SSL' ) . "\n" . EMAIL_TEXT_DATE_ORDERED . ' ' . tep_date_long( $check_status['date_purchased'] ) . "\n\n" . $notify_comments . sprintf( EMAIL_TEXT_STATUS_UPDATE, $orders_status_array[$POST['status']] );

        #PHPMailer Class
        if ( isset( $GET['php_mailer'] ) ) {
          require_once DIR_WS_MODULES . 'order_handler/PHPMailer/PHPMailerAutoload.php';
          $mail = new PHPMailer;

          $mail->isSMTP();                          # Set mailer to use SMTP
          $mail->Host       = PHPMAILER_HOST;       # Specify main and backup SMTP servers
          $mail->SMTPAuth   = PHPMAILER_SMTP_AUTH;  # Enable SMTP authentication
          $mail->Username   = PHPMAILER_USERNAME;   # SMTP username
          $mail->Password   = PHPMAILER_PASSWORD;   # SMTP password
          $mail->SMTPSecure = PHPMAILER_SECURE;     # Enable encryption, 'ssl' also accepted
          $mail->CharSet    = PHPMAILER_CHARSET;    # Character Encoding

          $mail->From       = PHPMAILER_FROM;       # Sender E-Mail address
          $mail->FromName   = PHPMAILER_FROMNAME;   # Sender Name

          $mail->addAddress( $check_status['customers_email_address'], $check_status['customers_name'] );  # Add a recipient

          $mail->WordWrap = PHPMAILER_WORDWRAP;
          $mail->ContentType = PHPMAILER_CONTENTTYPE;
          $mail->isHTML( PHPMAILER_IS_HTML ); # Set email format to HTML

          $mail->Subject     = EMAIL_TEXT_SUBJECT;
          $mail->Body        = nl2br( $email );
          $mail->AltBody     = strip_tags( $email );
        }

        if ( 'true' == SEND_EMAILS ) {

          #PHPMailer Class
          if ( isset( $GET['php_mailer'] ) ) {
            if ( ! $mail->send() ) {
              http_response_code( 400 );
              die( 'Mailer Error: ' . $mail->ErrorInfo );
            } else {
              $customer_notified = '1';
            }
            $mail->clearAddresses();

          } else {

            #PHP mail()
            tep_mail( $check_status['customers_name'], $check_status['customers_email_address'], EMAIL_TEXT_SUBJECT, $email, STORE_OWNER, STORE_OWNER_EMAIL_ADDRESS );
            $customer_notified = '1';
          }

        }
      }

      mysqli_prepared_query( "INSERT INTO $this->tablename_osh (orders_id, orders_status_id, date_added, customer_notified, comments) values (?, ?, now(), ?, ?)", "iiis", array( $GET['oID'], $POST['status'], $customer_notified, $POST['comments'] ) );

      $order_updated = true;
    }

    if ( true === $order_updated || false !== $error ) {
      $message .= SUCCESS_ORDER_UPDATED . "<br>";
      $comment_table =
        '<table border="1" cellspacing="0" cellpadding="5">' .
        '          <tr>' .
        '            <td class="smallText" align="center"><b>' . TABLE_HEADING_DATE_ADDED . '</b></td>' .
        '            <td class="smallText" align="center"><b>' . TABLE_HEADING_CUSTOMER_NOTIFIED . '</b></td>' .
        '            <td class="smallText" align="center"><b>' . TABLE_HEADING_STATUS . '</b></td>' .
        '            <td class="smallText" align="center"><b>' . TABLE_HEADING_COMMENTS . '</b></td>' .
        '          </tr>';

      $orders_history_query = mysqli_prepared_query( "SELECT orders_status_id, date_added, customer_notified, comments FROM $this->tablename_osh WHERE orders_id = ? ORDER BY date_added", "i", array( $GET['oID'] ) );
      if ( count( $orders_history_query ) ) {
        foreach ( $orders_history_query as $orders_history ) {
          $comment_table .=
            '          <tr>' . "\n" .
            '            <td class="smallText" align="center">' . tep_datetime_short( $orders_history['date_added'] ) . '</td>' . "\n" .
            '            <td class="smallText" align="center">';
          if ( $orders_history['customer_notified'] == '1' ) {
            $comment_table .=
              tep_image( DIR_WS_ICONS . 'tick.gif', ICON_TICK ) . "</td>\n";
          } else {
            $comment_table .=
              tep_image( DIR_WS_ICONS . 'cross.gif', ICON_CROSS ) . "</td>\n";
          }
          $comment_table .=
            '            <td class="smallText">' . $orders_status_array[$orders_history['orders_status_id']] . '</td>' . "\n" .
            '            <td class="smallText">' . nl2br( tep_db_output( $orders_history['comments'] ) ) . ' </td>' . "\n" .
            '          </tr>' . "\n";
        }
      } else {
        $comment_table .=
          '          <tr>' . "\n" .
          '            <td class="smallText" colspan="5">' . TEXT_NO_ORDER_HISTORY . '</td>' . "\n" .
          '          </tr>' . "\n";
      }
      $comment_table .=
        '</table>';

      #$message .= preg_replace( '/^.+:/', '', SUCCESS_ORDER_UPDATED );

      #Send Success Message
      die( json_encode( array( 'Result' => 'OK', 'Message' => $message, 'comments' => json_encode( $comment_table ) ) ) );
    } else {
      $message .= preg_replace( '/^.+:/', '', WARNING_ORDER_NOT_UPDATED );

      #Send Error Message
      #HTTP/1.1 400 Bad Request
      http_response_code( 400 );

      die( json_encode( array( 'Result' => 'ERROR', 'Message' => $message ) ) );
    }
  } # END update_order


  public function orders_total_update( $GET, $POST ) {
    global $languages_id, $currencies;
    header( 'Content-Type: application/json' );

    #Update Tax?
    $tax            = isset( $GET['title'] ) && ! empty( $GET['title'] ) ? true : false;

    #SQL Column Update
    $sql_column     = "UPDATE $this->tablename_ot SET " . tep_db_input( $GET['column'] ) . " = ? WHERE orders_id = ? AND class = ?";

    #Type Definitions
    $typeDef        = "sis";

    #Parameters Column Update
    $params_column  = array( $GET['new_value'], $GET['oID'], $GET['class'] );

    if ( $GET['column'] == 'value' ) {

      #Get the order's currency
      $currency_query = mysqli_prepared_query( "SELECT currency, currency_value from $this->tablename_o WHERE orders_id = ?", "i", array( $GET['oID'] ) );
      $currency       = $currency_query[0];

      #Construct new Currency representation of value
      $text           = $currencies->format( (float)$GET['new_value'], true, $currency['currency'], $currency['currency_value'] );

      #SQL Update Text
      $sql_text       = "UPDATE $this->tablename_ot SET text = ? WHERE orders_id = ? AND class = ?";

      #Parameters Update Text
      $params_text    = array( $text, $GET['oID'], $GET['class'] );

      #Update Tax if title is set
      if ( true === $tax ) {
        $sql_text    .= " AND title = ?";
        $sql_column  .= " AND title = ?";
        $typeDef     .= "s";
        array_push( $params_text,   $GET['title'] );
        array_push( $params_column, $GET['title'] );
      }

      #Execute Update Text
      mysqli_prepared_query( $sql_text, $typeDef, $params_text );
    }

    #Execute Update Column
    mysqli_prepared_query( $sql_column, $typeDef, $params_column );

    #Update Order Totals if not Update Tax
    if ( false === $tax )
      $this->tep_db_update_totals( $GET['oID'] );

    #Update Order Total if Update Tax
    if ( true === $tax ) {
      $orders_total_query = mysqli_prepared_query( "SELECT SUM(value) as value FROM orders_total WHERE orders_id = ? AND class != 'ot_total'", "i", array( $GET['oID'] ) );

      #Construct new Currency representation of value
      $text           = '<strong>' . $currencies->format( (float)$orders_total_query[0]['value'], true, $currency['currency'], $currency['currency_value'] ) . '</strong>';

      #SQL Update 'ot_total'
      $sql      = "UPDATE $this->tablename_ot SET value = ? WHERE orders_id = ? AND class = 'ot_total'";
      $sql_text = "UPDATE $this->tablename_ot SET text = ? WHERE orders_id = ? AND class = 'ot_total'";

      #Type Definitions
      $typeDef  = "si";

      #Parameters
      $params     = array( $orders_total_query[0]['value'], $GET['oID'] );
      $params_txt = array( $text, $GET['oID'] );

      #Execute Update Order Total
      mysqli_prepared_query( $sql, $typeDef, $params );
      mysqli_prepared_query( $sql_text, $typeDef, $params_txt );
    }

    #Update Product Totals if on 'Edit Order' Page
    if ( isset( $GET['edit_order'] ) ) {
      $products_total_html = $this->edit_order( $GET );
    } else {
      $products_total_html = $this->get_order( $GET, "all" );
    }

    #Send Success Message
    return json_encode( array( 'Result' => 'OK', 'Message' => 'Total Updated.', 'products_total_html' => json_encode( $products_total_html ) ) );
  } # END update_order


  public function update_product( $GET ) {
    header( 'Content-Type: application/json' );

    # Change the Price for Attributes (if any)
    if ( 'options' == $GET['field'] ) {
      # Determine Price Prefix
      $prefix = "+";
      if ( $GET['option_price'] < 0 ) {
        $prefix = "-";
      }

      # Format Option Price
      $option_price = round( abs( $GET['option_price'] ), 4 );

      $params = array( $GET['new_value'], $option_price, $prefix, $GET['pID'], $GET['extra'] );
      mysqli_prepared_query( "UPDATE $this->tablename_opa SET products_options_values = ?,  options_values_price = ?, price_prefix = ? WHERE orders_products_id = ? AND products_options = ?", "sdsis", $params );

      $attributes_query = mysqli_prepared_query( "SELECT products_price, products_name, options_values_price, price_prefix FROM $this->tablename_op op JOIN $this->tablename_opa opa USING (orders_id) WHERE opa.orders_products_id = ? AND op.orders_products_id = opa.orders_products_id", "i", array( $GET['pID'] ) );

      $price = $attributes_query[0]['products_price'];
      foreach ($attributes_query as $attributes) {
        $price += (float)( $attributes['price_prefix'] . $attributes['options_values_price'] );
      }

      $params = array( $price, $GET['pID'] );
      mysqli_prepared_query( "UPDATE $this->tablename_op SET final_price = ? WHERE orders_products_id = ?", "di", $params );
      $message = "Product Options Updated for '{$attributes_query[0]['products_name']}'.<br>New option price is $option_price for option {$GET['extra']}.";

    # Update Products Tax
    } elseif ( 'products_tax' == $GET['field'] ) {

      $params = array( $GET['new_value'], $GET['pID'] );
      mysqli_prepared_query( "UPDATE $this->tablename_op SET ".tep_db_input( $GET['field'] )." = ? WHERE orders_products_id = ?", "si", $params );

      $message = "Products Tax Updated to {$GET['new_value']}%.";

    } elseif ( stristr( $GET['field'], 'price' ) ) {

      $adapt_price_query = mysqli_prepared_query( "SELECT options_values_price FROM $this->tablename_opa WHERE orders_products_id = ? AND options_values_price != 0", "i", array( $GET['pID'] ) );
      if ( count( $adapt_price_query ) ) {
        $adapt_price = $adapt_price_query[0];
        $option_price = (float)$adapt_price['options_values_price'];
      } else {
        $option_price = 0;
      }
      if ( stristr( $GET['field'], '_excl' ) ) {
        $new_price = round( (float)$GET['new_value'], 4 );
      } else {
        $tax_query = mysqli_prepared_query( "SELECT products_tax FROM $this->tablename_op WHERE orders_products_id = ? AND products_tax != 0", "i", array( $GET['pID'] ) );
        if ( count( $tax_query ) ) {
          $tax_ = $tax_query[0];
          $percent = (float)$tax_['products_tax'];
          $percent = round( ( $percent/100 ), 4 );
          $percent = $percent + 1;
          $new_price = round( round( (float)$GET['new_value']/$percent, 4 ), 4 );
        } else {
          $new_price = round( (float)$GET['new_value'], 4 );
        }
      }

      $params = array( $new_price, ( $new_price - $option_price ), $GET['pID'] );
      mysqli_prepared_query( "UPDATE $this->tablename_op SET final_price = ?, products_price = ? WHERE orders_products_id = ?", "ddi", $params );
      $message = "Product Price Updated.";
    } else {
      if ( tep_not_null( $GET['field'] ) && tep_not_null( $GET['new_value'] ) ) {
        $params = array( $GET['new_value'], $GET['pID'] );
        mysqli_prepared_query( "UPDATE $this->tablename_op SET ".tep_db_input( $GET['field'] )." = ? WHERE orders_products_id = ?", "si", $params );
        if ( stristr( $GET['field'], 'products_quantity' ) )
          $message = "Product Quantity Updated.";
        else
          $message = "Order Updated.";
      }
    }

    #Update the Order Totals
    $this->tep_db_update_totals( $GET['oID'] );

    #Update Product Totals if on 'Edit Order' Page
    if ( isset( $GET['edit_order'] ) ) {
      $products_total_html = $this->edit_order( $GET );
    } else {
      $products_total_html = $this->get_order( $GET, "all" );
    }

    #Send Success Message
    return json_encode( array( 'Result' => 'OK', 'Message' => $message, 'products_total_html' => json_encode( $products_total_html ) ) );
  }


  public function eliminate_field( $GET, $POST ) {
    header( 'Content-Type: application/json' );

    #SQL
    $title         = urldecode( $POST['title'] );
    $sql_up        = "DELETE FROM $this->tablename_ot WHERE orders_id = ? AND title = ? AND class = ? limit 1";

    #Create the prepared statement
    $stmt_up       = $this->link->prepare( $sql_up );

    #Bind parameters to the query
    $bp_up         = $stmt_up->bind_param( "iss", $GET['oID'], $title, $POST['total_class'] );

    #Run the prepared statement
    $bp_ex         = $stmt_up->execute();

    #Close open connections
    $stmt_up->close();

    if ( false === $stmt_up || false === $bp_up || false === $bp_ex ) {
      #HTTP/1.1 400 Bad Request
      http_response_code( 400 );
      return json_encode( array( 'Result' => 'ERROR', 'Message' => $sql_up.' failed: ' . htmlspecialchars( $this->link->error ) ) );
    }


    $this->tep_db_update_totals( $GET['oID'] );

    #Update Product Totals if on 'Edit Order' Page
    if ( isset( $GET['edit_order'] ) ) {
      $products_total_html = $this->edit_order( $GET );
    } else {
      $products_total_html = $this->get_order( $GET, "all" );
    }

    #Send Success Message
    return json_encode( array( 'Result' => 'OK', 'Message' => "Field <strong style=\"font-style: oblique;\">\"" . urldecode( $POST['title'] ) . "\"</strong> Removed.", 'products_total_html' => json_encode( $products_total_html ) ) );

  }

  /* Delete Product Query */
  public function eliminate( $GET, $POST ) {
    header( 'Content-Type: application/json' );

    #SQL
    $sql        = "DELETE FROM $this->tablename_op WHERE orders_products_id = ?";
    $sql_attr   = "SELECT orders_products_attributes_id FROM $this->tablename_opa WHERE orders_products_id = ?";
    $sql_del    = "DELETE FROM $this->tablename_opa WHERE orders_products_attributes_id = ?";

    #Create the prepared statement
    $stmt       = $this->link->prepare( $sql );
    $stmt_attr  = $this->link->prepare( $sql_attr );

    #Bind parameters to the query
    $bp         = $stmt->bind_param( "i", $GET['pID'] );
    $bp_attr    = $stmt_attr->bind_param( "i", $GET['pID'] );

    #Execute Delete Product AND Select Attributes
    $bp         = $stmt->execute();
    $bp_attr    = $stmt_attr->execute();

    #Get Product Attributes
    if ( true === function_exists( 'mysqli_stmt_get_result' ) ) {
      # With Mysqlnd
      $res_attr = $stmt_attr->get_result();
      $attr     = $res_attr->fetch_all( MYSQLI_ASSOC );
    } else {
      # Without Mysqlnd
      $attr     = mysqli_get_res( $stmt_attr );
    }

    #If the product have attributes, then delete them
    $error = false;
    if ( count( $attr ) > 0 ) {

      #Bind & Prepare Attributes Statement
      $stmt_del   = $this->link->prepare( $sql_del );
      $bp_del     = $stmt_del->bind_param( "i", $orders_products_attributes_id );


      foreach ( $attr as $attributes ) {
        $orders_products_attributes_id = $attributes['orders_products_attributes_id'];
        #Execute Delete Attributes
        $bp_del   = $stmt_del->execute();
      }

      #Close Delete Attributes
      $stmt_del->close();

      #Error Handling Attributes
      if ( false === $stmt_del || false === $bp_del )
        $error = json_encode( array( 'Result' => 'ERROR', 'Message' => $sql_del.' failed: ' . htmlspecialchars( $this->link->error ) ) );
    }

    #Error Handling
    if ( false === $stmt || false === $bp )
      $error = json_encode( array( 'Result' => 'ERROR', 'Message' => $sql.' failed: ' . htmlspecialchars( $this->link->error ) ) );
    if ( false === $stmt_attr || false === $bp_attr )
      $error = json_encode( array( 'Result' => 'ERROR', 'Message' => $sql_attr.' failed: ' . htmlspecialchars( $this->link->error ) ) );

    if ( false !== $error ) {
      #HTTP/1.1 400 Bad Request
      http_response_code( 400 );
      return $error;
    }

    #Close open connections
    $stmt->close();
    $stmt_attr->close();

    #Update the Order totals
    $this->tep_db_update_totals( $GET['oID'] );

    #Update Product Totals if on 'Edit Order' Page
    if ( isset( $GET['edit_order'] ) ) {
      $products_total_html = $this->edit_order( $GET );
    } else {
      $products_total_html = $this->get_order( $GET, "all" );
    }

    #Return Success Message
    return json_encode( array( 'Result' => 'OK', 'Message' => "Product Removed.", 'products_total_html' => json_encode( $products_total_html ) ) );

  } # End Delete Product Query


  /* Add New Order Field */
  public function new_order_total( $GET ) {
    global $currencies;
    //header( 'Content-Type: application/json' );

    #SQL
    $sql            = "SELECT currency, currency_value, (SELECT max(sort_order) FROM $this->tablename_ot WHERE orders_id = o.orders_id AND class != 'ot_total') as maxim, (SELECT COUNT(class) FROM $this->tablename_ot WHERE orders_id = o.orders_id AND class like '%ot_extra_%') as class FROM $this->tablename_o o JOIN $this->tablename_ot USING (orders_id) WHERE orders_id = ? GROUP BY orders_id";

    #Execute Order Query
    $order_query    = mysqli_prepared_query( $sql, "i", array( $GET['oID'] ) );

    #Calculate new Sort Order and Append Tax Class if Tax is Selected
    $new_sort_order = (int)$order_query[0]['maxim'] + 1;
    $class          = 'ot_extra_' . ( (int)$order_query[0]['class'] + 1 );
    $class         .= ( '1' == $GET['add_tax'] ) ? ',' . $GET['tax_value'] : ',0';

    # Calculate Special Tax Class
    if ( '1' == $GET['add_tax'] ) {
      $others_tax_extra = tep_db_fetch_all( "SELECT tax_rate, tax_description FROM tax_rates tr WHERE tax_class_id = " . (int)$GET['tax_value'] );
      $GET['value'] += tep_calculate_tax( $GET['value'], $others_tax_extra[0]['tax_rate'] );
      $tax_class_title = $others_tax_extra[0]['tax_description'];

      # Calculate Standard Tax Class
    } else {
      $GET['value'] += tep_calculate_tax( $GET['value'], $others_tax[0]['tax_rate'] );
      $tax_class_title = $others_tax[0]['tax_class_title'];
    }

    #Create Currency String from Value
    $new_order_total_value_txt = $currencies->format( $GET['value'], true, $order_query[0]['currency'], $order_query[0]['currency_value'] );

    #Update Sort Order for Order Total
    mysqli_prepared_query( "UPDATE $this->tablename_ot SET sort_order = ? WHERE orders_id = ? AND class = 'ot_total'", "ii", array( ( (int)$new_sort_order + 1 ), $GET['oID'] ) );

    #Insert new Order Total Field
    mysqli_prepared_query( "INSERT INTO $this->tablename_ot (orders_id, title, text, value, class, sort_order) VALUES (?, ?, ?, ?, ?, ?)", "issdsi", array( $GET['oID'], $GET['title'] . ':', $new_order_total_value_txt, round( (float)$GET['value'], 4 ), $class, $new_sort_order ) );

    #Update the Order Totals
    $this->tep_db_update_totals( $GET['oID'] );

    #Update Product Totals if on 'Edit Order' Page
    if ( isset( $GET['edit_order'] ) ) {
      $products_total_html = $this->edit_order( $GET );
    } else {
      $products_total_html = $this->get_order( $GET, true );
    }


    #Send Success Message
    return json_encode( array( 'Result' => 'OK', 'Message' => "New Field Added.", 'products_total_html' => json_encode( $products_total_html ) ) );

  } # END new_order_total


  function update_shipping_method( $GET ) {
    global $cart, $currency, $currencies, $language, $order, $shipping_weight, $shipping_num_boxes;
    include_once DIR_WS_CLASSES . 'order.php';
    $order = new order( $GET['oID'] );

//    $GET['new_value'] = 'zones';

    # Get the Countries iso_code_2
    # Set Customers Country and Zone
    $order_query = mysqli_prepared_query( "SELECT currency, countries_id, delivery_address_format_id, delivery_country, countries_iso_code_2, countries_iso_code_3 FROM $this->tablename_o o JOIN $this->tablename_co c ON (c.countries_name = o.delivery_country) WHERE orders_id = ? LIMIT 1", "i", array( $GET['oID'] ) );

    $order->delivery['country'] =
    array(
      'id'         => $order_query[0]['countries_id'],
      'title'      => $order_query[0]['delivery_country'],
      'iso_code_2' => $order_query[0]['countries_iso_code_2'],
      'iso_code_3' => $order_query[0]['countries_iso_code_3'],
    );
    $order->delivery['zone_id'] = $order_query[0]['delivery_address_format_id'];
    $currency = $order_query[0]['currency'];

    $cart  = new shoppingCart();
    $order->content_type = $cart->get_content_type();

    $shopping_cart = mysqli_prepared_query( "SELECT * FROM $this->tablename_op WHERE orders_id = ?", "i", array( $GET['oID'] ) );
    foreach ( $shopping_cart as $key => $product ) {
      $cart->add_cart($product['products_id'], $product['products_quantity'], '', false);
    }

    $shipping_num_boxes = 1;
    $cart->weight = $cart->show_weight();
    $shipping_weight = $cart->show_weight();

    if ( defined( 'MODULE_SHIPPING_INSTALLED' ) && tep_not_null( MODULE_SHIPPING_INSTALLED ) ) {
      $this->modules = explode( ';', MODULE_SHIPPING_INSTALLED );

      $include_modules = array();

      reset( $this->modules );
      while ( list( , $value ) = each( $this->modules ) ) {
        $class = substr( $value, 0, strrpos( $value, '.' ) );
        $include_modules[] = array( 'class' => $class, 'file' => $value );
      }

      for ( $i=0, $n=sizeof( $include_modules ); $i<$n; $i++ ) {
        include_once DIR_FS_CATALOG . DIR_WS_LANGUAGES . $language . '/modules/shipping/' . $include_modules[$i]['file'];
        include_once DIR_FS_CATALOG . DIR_WS_MODULES . 'shipping/' . $include_modules[$i]['file'];

        $modules[$i] = new $include_modules[$i]['class'];
        $modules[$i]->quote();

        if ( $GET['new_value'] == $modules[$i]->code ) {

          #Get the order's currency
          $currency = mysqli_prepared_query( "SELECT currency, currency_value from $this->tablename_o WHERE orders_id = ?", "i", array( $GET['oID'] ) );

          #Construct new Currency representation of value
          $text           = $currencies->format( (float)$GET['new_value'], true, $currency[0]['currency'], $currency[0]['currency_value'] );

          #Construct new Currency representation of value
          $shipping['title'] = ( isset( $modules[$i]->quotes['methods'][0]['module'] ) ? $modules[$i]->quotes['methods'][0]['module'] : $modules[$i]->title ) . ' (' . $modules[$i]->quotes['methods'][0]['title'] . '):';
          $shipping['tax']   = $modules[$i]->quotes['tax'];
          $shipping['cost']  = $modules[$i]->quotes['methods'][0]['cost'];
          $shipping['text']  = $currencies->format( (float)$shipping['cost'], true, $currency[0]['currency'], $currency[0]['currency_value'] );

          // if ( null !== $modules[$i]->quotes['error'] ) {
          //   #HTTP/1.1 404 Not Found
          //   http_response_code( 405 );
          //   die( $modules[$i]->quotes['error'] );
          // }

          if ( null !== $modules[$i]->quotes['error'] ) {
            #HTTP/1.1 405 Method Not Allowed
            http_response_code( 405 );
            $message = "<br><p>Problem Selecting '" . $modules[$i]->title . "' Shipping Method for this Order:</p><br><p><i>" . $modules[$i]->quotes['error'] . "</i></p>";
            die( $message );
          }

          # Check for existing ot_shipping
          $existing_shipping = mysqli_prepared_query( "SELECT orders_total_id FROM $this->tablename_ot WHERE orders_id = ? AND class = 'ot_shipping'", "i", array( $GET['oID'] ) );

          $params  = array( $shipping['title'], $shipping['text'], $shipping['cost'], $GET['oID'] );
          $typeDef = "ssdi";

          if ( ! empty( $existing_shipping ) ) {
            array_unshift( $params, $existing_shipping[0]['orders_total_id'] );
            mysqli_prepared_query( "REPLACE INTO $this->tablename_ot (orders_total_id, title, text, value, orders_id, class) VALUES (?, ?, ?, ?, ?, 'ot_shipping')", "issdi", $params );
          } else {
            mysqli_prepared_query( "REPLACE INTO $this->tablename_ot (title, text, value, orders_id, class) VALUES (?, ?, ?, ?, 'ot_shipping')", "ssdi", $params );
          }

          break;
        }
      }
    }

    #Update the Order Totals
    $this->tep_db_update_totals( $GET['oID'] );

    #Update Product Totals if on 'Edit Order' Page
    if ( isset( $GET['edit_order'] ) ) {
      $products_total_html = $this->edit_order( $GET );
    } else {
      $products_total_html = $this->get_order( $GET, "all" );
    }

    #Send Success Message
    return json_encode( array( 'Result' => 'OK', 'Message' => 'Shipping Method Updated!', 'products_total_html' => json_encode( $products_total_html ) ) );
  }


  /* Update Specific Order Field */
  public function update_order_field( $GET, $POST ) {
    global $languages_id;
    header( 'Content-Type: application/json' );

    #SQL
    $field         = tep_db_input( $GET['field'] );
    $sql_up        = "UPDATE $this->tablename_o SET $field = ? WHERE orders_id = ?";

    #Create the prepared statement
    $stmt_up       = $this->link->prepare( $sql_up );

    #Bind parameters to the query
    $bp_up         = $stmt_up->bind_param( "si", $GET['new_value'], $GET['oID'] );

    #Run the prepared statement
    $bp_ex         = $stmt_up->execute();

    #Close open connections
    $stmt_up->close();

    #Error Handling
    $error = false;
    if ( false === $stmt_up || false === $bp_up || false === $bp_ex )
      $error = json_encode( array( 'Result' => 'ERROR', 'Message' => $sql_up.' failed: ' . htmlspecialchars( $this->link->error ) ) );

    if ( false !== $error ) {
      #HTTP/1.1 400 Bad Request
      http_response_code( 400 );
      return $error;
    }

    if ( 'payment_method' == $GET['field'] ) {
      #Update the Order Totals
      $this->tep_db_update_totals( $GET['oID'] );

      #Update Product Totals if on 'Edit Order' Page
      if ( isset( $GET['edit_order'] ) ) {
        $products_total_html = $this->edit_order( $GET );
      } else {
        $products_total_html = $this->get_order( $GET, "all" );
      }

      #Send Success Message
      return json_encode( array( 'Result' => 'OK', 'Message' => 'Field updated.', 'products_total_html' => json_encode( $products_total_html ) ) );
    }

    return json_encode( array( 'Result' => 'OK', 'Message' => 'Field updated.' ) );

  } # END update_order_field


  /* jTable Edit Order / Update Order Fields */
  public function update_order_fields( $GET, $POST ) {
    global $language;
    header( 'Content-Type: application/json' );

    #SQL
    $sql_up        = "UPDATE $this->tablename_o SET
    customers_name = ?,
    customers_company = ?,
    customers_email_address = ?,
    delivery_street_address = ?,
    delivery_postcode = ?,
    delivery_state = ?,
    delivery_city = ?,
    delivery_country = ?,
    customers_telephone = ?,
    payment_method = ? WHERE orders_id = ?";

    # load selected payment module

    $payment                      = unserialize( stripslashes( $POST['payment_method_code'] ) );
    $POST['payment_method']       = $payment[1];

    #    $shipping                     = unserialize( stripslashes( $POST['shipping_module'] ) );
    #    $POST['shipping_module']      = $shipping[0];

    $records                      = array();

    #Parameters on Prepared Statements must be a &$reference to a $variable
    $params        =
      array(
      &$POST['customers_name'],
      &$POST['customers_company'],
      &$POST['customers_email_address'],
      &$POST['delivery_street_address'],
      &$POST['delivery_postcode'],
      &$POST['delivery_state'],
      &$POST['delivery_city'],
      &$POST['delivery_country'],
      &$POST['customers_telephone'],
      &$POST['payment_method'],
      &$POST['orders_id'],
    );

    #Create Records for jTable
    foreach ( $POST as $key => $value ) {
      $records[$key] = $value;
    }

    #Define Parameters Type. Types: s = string, i = integer, d = double,  b = binary
    $typeDef        = "ssssssssssi";
    array_unshift( $params, $typeDef );

    #Create the prepared statement
    $error = false;
    if ( ! ( $stmt_up  = $this->link->prepare( $sql_up ) ) )
      $error = json_encode( array( 'Result' => 'ERROR', 'Message' => $sql_up.' failed: ' . htmlspecialchars( $this->link->error ) ) );

    if ( false !== $error ) {
      #HTTP/1.1 400 Bad Request
      http_response_code( 400 );
      return $error;
    }

    #Bind parameters to the query
    $ref            = new ReflectionClass( 'mysqli_stmt' );
    $method         = $ref->getMethod( "bind_param" );
    $method->invokeArgs( $stmt_up, $params );

    #Run the prepared statement
    if ( !( $bp_ex    = $stmt_up->execute() ) )
      $error = json_encode( array( 'Result' => 'ERROR', 'Message' => $sql_up.' failed: ' . htmlspecialchars( $this->link->error ) ) );

    if ( false !== $error ) {
      #HTTP/1.1 400 Bad Request
      http_response_code( 400 );
      return $error;
    }

    #Close open connections
    $stmt_up->close();

    return json_encode( array( "Result" => "OK", "Record" => $records ) );

  } # END jTable update_order_fields


  # Search Orders
  public function search_orders( $GET ) {
    global $languages_id;
    header( 'Content-Type: application/json' );

    $params = array( $GET['q'], $GET['q'], '%'.$GET['q'].'%', '%'.$GET['q'].'%', '%'.$GET['q'].'%', '%'.$GET['q'].'%', $GET['page_limit'] );
    $orders_query = mysqli_prepared_query( "SELECT orders_id, customers_id, customers_name, customers_email_address, date_purchased FROM $this->tablename_o WHERE (orders_id = ? OR customers_id = ? OR customers_name LIKE ? OR customers_email_address LIKE ? OR delivery_name LIKE ? OR billing_name LIKE ?) LIMIT ?", "iissssi", $params );

    if ( count( $orders_query ) ) {
      $return_arr = array();
      foreach ( $orders_query as $orders ) {
        $row_array['id'] = tep_href_link( "get_table.php", 'oID=' . $orders['orders_id'] . '&cID=' . $orders['customers_id'] . '&action=edit_order' );
        $row_array['text'] = $orders['orders_id'] . ' - ' . $orders['customers_name'] . ' - ' . $orders['date_purchased'];

        array_push( $return_arr, $row_array );
      }

    } else {
      $return_arr = array( 'value' => PRODUCTS_SEARCH_NO_RESULTS );
    }
    return json_encode( $return_arr );

  } # End Search Orders


  /* Customer Search from jQuery Autocomplete */
  public function search_customers( $GET ) {
    header( 'Content-Type: application/json' );

    #Firstname only OR Firstname Lastname?
    $customers_name_test = preg_match_all( '/([^\s]+)/', $GET['term'], $customers_name );

    #Firstname and Lastname
    if ( count( $customers_name[1] ) > 1 ) {
      $params = array( 'ss', '%'.$customers_name[1][0].'%', '%'.$customers_name[1][1].'%' );
      $search_query = " AND (customers_firstname like ? AND customers_lastname like ?) ";
    } else {
      #Firstname only OR Email Address OR customers_id
      $params = array( 'sss', '%'.$customers_name[1][0].'%', '%'.$customers_name[1][0].'%', '%'.$customers_name[1][0].'%' );
      $search_query = " AND (customers_firstname like ? OR customers_email_address like ? OR c.customers_id like ?) ";
    }

    #SQL
    $sql_se = "SELECT c.customers_id, c.customers_firstname, c.customers_lastname, CONCAT(c.customers_firstname, ' ', c.customers_lastname) AS customers_name, CONCAT(ab.entry_street_address, ', ', ab.entry_postcode, ', ', ab.entry_city) AS customers_address, ab.entry_company, ab.entry_city, z.zone_code FROM $this->tablename_c c, $this->tablename_ab ab LEFT JOIN $this->tablename_z z ON (ab.entry_zone_id = z.zone_id) WHERE c.customers_default_address_id = ab.address_book_id $search_query ORDER BY entry_company, customers_lastname";

    $stmt_se = $this->link->stmt_init();

    #Create the prepared statement
    if ( !$stmt_se->prepare( $sql_se ) )
      die( json_encode( array( 'status' => 'ERROR', 'Message' => 'Preparing the SELECT statement failed: ' . htmlspecialchars( $this->link->error ) ) ) );

    #Bind parameters to the query
    $ref = new ReflectionClass( 'mysqli_stmt' );
    $method = $ref->getMethod( "bind_param" );
    $method->invokeArgs( $stmt_se, $params );

    #Run the prepared statement
    if ( !$stmt_se->execute() )
      die( json_encode( array( 'status' => 'ERROR', 'Message' => "Executing $sql_se failed: " . htmlspecialchars( $this->link->error ) ) ) );

    #Return jQuery Autocomplete Array
    $return_arr = array();

    #Get the customers info
    if ( true === function_exists( 'mysqli_stmt_get_result' ) ) {
      # With Mysqlnd
      $res            = $stmt_se->get_result();
      while ( ( $row  = $res->fetch_assoc() ) ) {
        $return_arr[] =
          array(
          'id'        => tep_href_link( "get_table.php", 'action=get_customer&cID=' . (int)$row['customers_id'] ),
          'value'     => $row['customers_address'] . ': ' . $row['customers_address'],
        );
      }

      #Close open connections
      $stmt_se->close();
    } else {
      # Without Mysqlnd
      $res            = mysqli_get_res( $stmt_se );
      foreach ( $res as $row ) {
        $return_arr[] =
          array(
          'id'        => tep_href_link( "get_table.php", 'action=get_customer&cID=' . (int)$row['customers_id'] ),
          'value'     => $row['customers_name'] . ': ' . $row['customers_address'],
        );
      }
    }

    return json_encode( $return_arr );

  } # End Search Customers


  /* Get Customers Info */
  public function get_customer( $GET ) {
    header( 'Content-Type: application/json' );

    #SQL Query
    $sql_account      = "SELECT c.customers_id, c.customers_gender, c.customers_firstname, c.customers_lastname, c.customers_email_address, c.customers_telephone, c.customers_fax, ab.entry_company, ab.entry_street_address, ab.entry_suburb, ab.entry_postcode, ab.entry_city, ab.entry_state, ab.entry_country_id, ab.entry_zone_id FROM $this->tablename_c c LEFT JOIN $this->tablename_ab ab ON (c.customers_id = ab.customers_id) WHERE c.customers_id = ?";

    #Prepare statements
    $stmt_account     = $this->link->prepare( $sql_account );
    if ( $stmt_account === false )
      die( json_encode( array( 'status' => 'ERROR', 'Message' => 'Preparing '.$sql_account.' failed: ' . htmlspecialchars( $this->link->error ) ) ) );

    #Bind parameters. Types: s = string, i = integer, d = double,  b = binary
    $stmt_account->bind_param( 'i', $GET['cID'] );

    #Execute statement
    $stmt_account->execute();

    if ( true === function_exists( 'mysqli_stmt_get_result' ) ) {
      # With Mysqlnd
      $rs = $stmt_account->get_result();
      $arr = $rs->fetch_all( MYSQLI_ASSOC );

      #Close open connections
      $stmt_account->close();
    } else {
      # Without Mysqlnd
      $arr            = mysqli_get_res( $stmt_account );
    }

    return json_encode( $arr[0] );
  } # END get_customer


  public function create_new_order( $GET, $POST ) {
    global $currencies, $currency, $language, $cart, $order;
    header( 'Content-Type: application/json' );

    $cart = new shoppingCart();

    # Get the Countries iso_code_2
    $sql_country            = "SELECT * FROM $this->tablename_co WHERE countries_name = ?";
    $country                = mysqli_prepared_query( $sql_country, "s", array( $POST['delivery_country'] ) );

    require_once DIR_FS_DOCUMENT_ROOT . DIR_WS_CLASSES . 'order.php';
    $order = new order;

    $payment                      = unserialize( stripslashes( $POST['payment_method_code'] ) );
    $POST['payment_method']       = $payment[1];

    $shipping                     = unserialize( stripslashes( $POST['shipping_module'] ) );
    $POST['shipping_module']      = $shipping[0];

    if ( !tep_session_is_registered( 'currency' ) ) tep_session_register( 'currency' );
    $currency_array = explode( ",", $POST['Currency'] );
    $currency = $currency_array[0];
    $currency_value = $currency_array[1];

    $order->info['subtotal']        = 0;
    $order->info['currency']        = $currency;
    $order->info['currency_value']  = $currency_value;
    $order->info['tax']             = 0;
    $order->info['total']           = 0;
    #$order->info['shipping_cost']   = 0;
    #$order->info['shipping_method'] = $POST['shipping_module'];
    $order->delivery['country']     =
      array(
      'id'                            => $country[0]['countries_id'],
      'title'                         => $country[0]['countries_name'],
      'iso_code_2'                    => $country[0]['countries_iso_code_2'],
      'iso_code_3'                    => $country[0]['countries_iso_code_3']
    );
    $order->delivery['country_id']  = $country[0]['countries_id'];
    $order->delivery['format_id']   = $country[0]['address_format_id'];
    $format_id                      = $country[0]['address_format_id'];

    # Time to go to hell ...
    $dir = getcwd();
    chdir( "../" );

    require_once DIR_WS_CLASSES . 'order_total.php';
    $order_total_modules = new order_total;

    # load selected payment module
    require_once DIR_WS_CLASSES . 'payment.php';

    $payment = $this->get_payment_modules( $POST['payment_method'] );

    $GLOBALS['payment'] = $payment['code'];
    $payment_modules = new payment( $payment['code'] );

    $include_modules = array( 'class' => $payment['code'], 'file' => $payment['code'] . '.php' );

    include_once DIR_WS_LANGUAGES . $language . '/modules/payment/' . $include_modules['file'];
    include_once DIR_WS_MODULES . 'payment/' . $include_modules['file'];

    $payment_module = new $include_modules['class'];

    # load the selected shipping module
    $include_modules = array( 'class' => $POST['shipping_module'], 'file' => $POST['shipping_module'] . '.php' );

    include_once DIR_WS_LANGUAGES . $language . '/modules/shipping/' . $include_modules['file'];
    include_once DIR_WS_MODULES . 'shipping/' . $include_modules['file'];

    $shipping_module  = new $include_modules['class'];

    # because of how modules depends on variable, we have to 'spoof' the orders total process
    require_once DIR_WS_CLASSES . 'shipping.php';
    $shipping_modules = new shipping( array( "id" => $shipping_module->code . '_' . $shipping_module->code ) );
    $quotes           = $shipping_modules->quote();
    $sort_order       = $GLOBALS[ $POST['shipping_module'] ]->sort_order;

    $shipping_tax     = tep_get_tax_rate( $shipping_module->tax_class, $country[0]['countries_id'] );
    $shipping_tax     = tep_calculate_tax( $quotes[0]['methods'][0]['cost'], $shipping_tax );
    $tax_description  = tep_get_tax_description( $shipping_module->tax_class, $country[0]['countries_id'] );

    $shipping =
      array(
      'id'          => $quotes[0]['id'],
      'title'       => $quotes[0]['module'] . ' (' . $quotes[0]['methods'][0]['title'] . ')',
      'cost'        => $quotes[0]['methods'][0]['cost'],// + $shipping_tax,
      'tax'         => $shipping_tax,
      'sort_order'  => $sort_order
    );


    $order->info['tax_groups']      = array( $tax_description => $shipping['tax'] );
    $order->info['shipping_cost']   = $shipping['cost'];
    //$order->info['tax']             += $shipping['tax'];
    $order->info['payment_method']  = $payment_modules->selected_module;
    $order->info['shipping_method'] = $shipping['title'];
    $order->info['total']           += $shipping['cost'];

    # "fix shipping"
    $shipping_total =
      array(
      'code'       => 'ot_shipping',
      'title'      => $shipping['title'] . ":",
      'text'       => $currencies->format( $shipping['cost'], true, $currency, $currency_value ),
      'value'      => $shipping['cost'],
      'sort_order' => $sort_order,
    );

    // require_once DIR_WS_CLASSES . 'order_total.php';
    // $order_total_modules = new order_total;

    $shipping_total['sort_order'] = $GLOBALS['ot_shipping']->sort_order;
    $GLOBALS['ot_shipping']->output[] = $shipping_total;
    $GLOBALS['shipping']['id'] = $quotes[0]['id'] . '_' . $quotes[0]['methods'][0]['id'];

    # Try to determine Tax on Payment Method Cost
    $order_total_extra_modules = array();
    if ( defined( 'MODULE_ORDER_TOTAL_INSTALLED' ) && tep_not_null( MODULE_ORDER_TOTAL_INSTALLED ) ) {
      $modules = explode( ';', MODULE_ORDER_TOTAL_INSTALLED );

      reset( $modules );
      while ( list( , $value ) = each( $modules ) ) {
        include_once DIR_FS_CATALOG . DIR_WS_LANGUAGES . $language . '/modules/order_total/' . $value;
        include_once DIR_FS_CATALOG . DIR_WS_MODULES . 'order_total/' . $value;

        $class = substr( $value, 0, strrpos( $value, '.' ) );

        $module = new $class;
        $order_total_extra_modules[ $module->code ] = (int)$module->sort_order;
      }
    }
    
    $backup_order['shipping_cost'] = $order->info['shipping_cost'];
    $backup_order['tax']           = $order->info['tax'];
    $backup_order['tax_groups']    = $order->info['tax_groups'];
    $backup_order['total']         = $order->info['total'];


    # Get all available Payment Quotes
    $payment_module       = $this->get_payment_modules( $POST['payment_method'] );
    $new_taxes            = array();

    foreach ( $order_total_extra_modules as $order_total => $tot ) {
      if (
           'ot_tax'      != $order_total
        && 'ot_shipping' != $order_total
        && 'ot_subtotal' != $order_total
        && 'ot_total'    != $order_total
        && false         === strpos( $order_total, 'ot_extra' )
      ) {

______________________________________________________

 

* End Part 7 *

______________________________________________________

Link to comment
Share on other sites

Replace ./admin/includes/modules/order_handler/ajax_update.php With This: (2/2 Part of this File)

        $GLOBALS['payment'] = $payment_module['code'];
        include_once DIR_FS_CATALOG . DIR_WS_LANGUAGES . $language . '/modules/order_total/' . $order_total . '.php';
        include_once DIR_FS_CATALOG . DIR_WS_MODULES . 'order_total/' . $order_total . '.php';

        # Reset Tax Variables
        $order->info['tax']        = 0;
        $order->info['subtotal']   = 0;
        $order->info['total']      = 0;
        $order->info['tax_groups'] = '';


        $module = new $order_total;
        $module->process();

        $payment_tax = $order->info['tax_groups'];

      }
    }
    $order->info['shipping_cost'] = $backup_order['shipping_cost'];
    $order->info['tax']           = $backup_order['tax'];
    $order->info['total']         = $backup_order['total'];

    $order_totals = $order_total_modules->process();

    # finally.. sort the f*ckers and then we're done
    usort( $order_totals, function( $a, $b ) {
        return $a['sort_order'] - $b['sort_order'];
      } );

    # ... and back again. What the hell happened?
    chdir( $dir );

    $sql_data_array =
      array(
      'customers_id'                => ( isset( $POST['customers_id'] ) ? $POST['customers_id'] : '' ),
      'customers_name'              => $POST['customers_firstname'] . " " . $POST['customers_firstname'],
      'customers_company'           => ( isset( $POST['delivery_company'] ) ? $POST['delivery_company'] : '' ),

      'customers_street_address'    => ( isset( $POST['delivery_street_address'] ) ? $POST['delivery_street_address'] : '' ),
      'customers_suburb'            => ( isset( $POST['entry_suburb'] ) ? $POST['entry_suburb'] : '' ),
      'customers_city'              => ( isset( $POST['delivery_city'] ) ? $POST['delivery_city'] : '' ),
      'customers_postcode'          => ( isset( $POST['delivery_postcode'] ) ? $POST['delivery_postcode'] : '' ),
      'customers_state'             => ( isset( $POST['entry_state'] ) ? $POST['entry_state'] : '' ),
      'customers_country'           => ( isset( $POST['delivery_country'] ) ? $POST['delivery_country'] : '' ),

      'customers_telephone'         => ( isset( $POST['customers_telephone'] ) ? $POST['customers_telephone'] : '' ),
      'customers_email_address'     => ( isset( $POST['customers_email_address'] ) ? $POST['customers_email_address'] : '' ),
      'customers_address_format_id' => ( isset( $format_id ) ? $format_id : '' ),
      'delivery_name'               => $POST['customers_firstname'] . " " . $POST['customers_firstname'],
      'delivery_company'            => ( isset( $POST['company'] ) ? $POST['company'] : '' ),
      'delivery_street_address'     => ( isset( $POST['delivery_street_address'] ) ? $POST['delivery_street_address'] : '' ),
      'delivery_suburb'             => ( isset( $POST['entry_suburb'] ) ? $POST['entry_suburb'] : '' ),
      'delivery_city'               => ( isset( $POST['delivery_city'] ) ? $POST['delivery_city'] : '' ),
      'delivery_postcode'           => ( isset( $POST['delivery_postcode'] ) ? $POST['delivery_postcode'] : '' ),
      'delivery_state'              => ( isset( $POST['entry_state'] ) ? $POST['entry_state'] : '' ),
      'delivery_country'            => ( isset( $POST['delivery_country'] ) ? $POST['delivery_country'] : '' ),
      'delivery_address_format_id'  => ( isset( $format_id ) ? $format_id : '' ),
      'billing_name'                => $POST['customers_firstname'] . " " . $POST['customers_firstname'],
      'billing_company'             => ( isset( $POST['company'] ) ? $POST['company'] : '' ),
      'billing_street_address'      => ( isset( $POST['delivery_street_address'] ) ? $POST['delivery_street_address'] : '' ),
      'billing_suburb'              => ( isset( $POST['entry_suburb'] ) ? $POST['entry_suburb'] : '' ),
      'billing_city'                => ( isset( $POST['delivery_city'] ) ? $POST['delivery_city'] : '' ),
      'billing_postcode'            => ( isset( $POST['delivery_postcode'] ) ? $POST['delivery_postcode'] : '' ),
      'billing_state'               => ( isset( $POST['entry_state'] ) ? $POST['entry_state'] : '' ),
      'billing_country'             => ( isset( $POST['delivery_country'] ) ? $POST['delivery_country'] : '' ),
      'billing_address_format_id'   => ( isset( $format_id ) ? $format_id : '' ),
      'date_purchased'              => 'now()',
      'orders_status'               => DEFAULT_ORDERS_STATUS_ID,
      'currency'                    => ( isset( $currency ) ? $currency : '' ),
      'currency_value'              => ( isset( $currency_value ) ? $currency_value : '' ),
      'payment_method'              => $POST['payment_method'],
      #'shipping_module'             => (isset($POST['shipping_module']) ? $POST['shipping_module'] : ''),
    );

    #old
    tep_db_prepared_perform( TABLE_ORDERS, $sql_data_array );
    $insert_id = tep_db_insert_id();


    $sql_data_array = array(
      'orders_id' => $insert_id,
      'orders_status_id' => DEFAULT_ORDERS_STATUS_ID,
      'date_added' => 'now()'
    );
    tep_db_prepared_perform( TABLE_ORDERS_STATUS_HISTORY, $sql_data_array );
    #var_dump($sql_data_array);



    if ( defined( 'MODULE_ORDER_TOTAL_INSTALLED' ) && tep_not_null( MODULE_ORDER_TOTAL_INSTALLED ) ) {
      $order_total_modules = explode( ';', MODULE_ORDER_TOTAL_INSTALLED );
      $co_modules = array();
      $total = 0;

      for ( $i=0, $n=sizeof( $order_totals ); $i<$n; $i++ ) {
        $sql_data_array = array( 'orders_id' => $insert_id,
          'title' => $order_totals[$i]['title'],
          'text' => $order_totals[$i]['text'],
          'value' => $order_totals[$i]['value'],
          'class' => $order_totals[$i]['code'],
          'sort_order' => $order_totals[$i]['sort_order'] );
        tep_db_prepared_perform( TABLE_ORDERS_TOTAL, $sql_data_array );
        #var_dump($sql_data_array);
      }
    }

    $message = "New order created for customer " . trim( $POST['customers_firstname'] . ' ' . $POST['customers_lastname'] ) . " with order number " . $insert_id . ".";

    $records = json_decode( $this->get_orders( $GET, $POST, $insert_id, $message ) );

    return json_encode( array( "Result" => "OK", "Record" => $records->Records[0] ) );
  }



  public function attributes( $GET, $POST ) {
    global $currencies, $languages_id;
    header( 'Content-Type: application/json' );

    #We create an AJAX form
    $attributes = "";

    $params = array( $GET['prID'], $languages_id );
    $products_attributes_query = mysqli_prepared_query( "SELECT count(*) as total FROM $this->tablename_po popt, $this->tablename_a patrib WHERE patrib.products_id = ? AND patrib.options_id = popt.products_options_id AND popt.language_id = ?", "ii", $params );

    $products_tax_class_id = mysqli_prepared_query( "SELECT products_tax_class_id FROM $this->tablename_p WHERE products_id = ?", "i", array( $GET['prID'] ) );

    if ( $products_attributes_query[0]['total'] > 0 ) {
      $attributes .= '<table border="0" cellspacing="0" cellpadding="2" class="dataTableRow" width="100%"><tr><td class="dataTableContent" colspan="2">' . TEXT_PRODUCT_OPTIONS . '</td>            </tr>';

      $params = array( $GET['prID'], $languages_id );
      $products_options_name_query = mysqli_prepared_query( "SELECT distinct popt.products_options_id, popt.products_options_name FROM $this->tablename_po popt, $this->tablename_a patrib WHERE patrib.products_id = ? AND patrib.options_id = popt.products_options_id AND popt.language_id = ? ORDER BY popt.products_options_name", "ii", $params );

      foreach ( $products_options_name_query as $products_options_name ) {
        $products_options_array = array();

        $params = array( $GET['prID'], $products_options_name['products_options_id'], $languages_id );
        $products_options_query = mysqli_prepared_query( "SELECT pov.products_options_values_id, pov.products_options_values_name, pa.options_values_price, pa.price_prefix FROM $this->tablename_a pa, $this->tablename_pov pov WHERE pa.products_id = ? AND pa.options_id = ? AND pa.options_values_id = pov.products_options_values_id AND pov.language_id = ?", "iii", $params );

        foreach ( $products_options_query as $products_options ) {
          $products_options_array[] = array( 'id' => $products_options['products_options_values_id'], 'text' => $products_options['products_options_values_name'] );
          if ( $products_options['options_values_price'] != '0' ) {
            $products_options_array[sizeof( $products_options_array )-1]['text'] .= ' (' . $products_options['price_prefix'] . $currencies->display_price( $products_options['options_values_price'], tep_get_tax_rate( $products_tax_class_id[0] ) ) .') ';
          }
        }
        $attributes .= '<tr><td class="main">' . $products_options_name['products_options_name'] . ':</td><td class="main">' . tep_draw_pull_down_menu( 'atrid_' . $products_options_name['products_options_id'], $products_options_array ) . '</td></tr>';
      }
      $button = '<span><button type="submit" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-icon-secondary ui-priority-secondary" role="button" aria-disabled="false" style="margin-bottom: 5px;"><span class="ui-button-icon-secondary ui-icon ui-icon-disk"></span><span class="ui-button-text">' . IMAGE_CONFIRM . '</span></button></span>';

      $attributes .= '<tr><td colspan="2">' . $button . '</td></tr></table>';
    } else {
      $button = '<span><button type="submit" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-icon-secondary ui-priority-secondary" role="button" aria-disabled="false" style="margin-bottom: 5px;"><span class="ui-button-icon-secondary ui-icon ui-icon-disk"></span><span class="ui-button-text">' . IMAGE_CONFIRM . '</span></button></span>';
      $attributes .= $button;
    }
    return $attributes;

  } # End Update Attributes


  public function set_attributes( $GET, $POST ) {
    global $languages_id;
    header( 'Content-Type: application/json' );

    $attributes = array();
    $products_id = 0;
    $products_quantity = 0;
    foreach ( $POST as $key => $value ) {
      if ( $key == 'products_id' ) {
        $products_id = $value;
      } elseif ( $key == 'products_quantity' ) {
        $products_quantity = $value;
      } elseif ( stristr( $key, 'trid_' ) ) {
        $attributes[] = array( substr( $key, 6 ), $value );
      }
    }
    $orders_id = $GET['oID'];

    $params = array( $products_id, $languages_id );
    $product_info = mysqli_prepared_query( "SELECT p.products_model, pd.products_name, p.products_price, p.products_tax_class_id FROM $this->tablename_p p left join $this->tablename_pd pd on p.products_id = pd.products_id WHERE p.products_id = ? AND pd.language_id = ?", "ii", $params );

    if ( DISPLAY_PRICE_WITH_TAX == 'true' ) {

      $tax_query = mysqli_prepared_query( "SELECT tax_rate, tax_description FROM $this->tablename_tr WHERE tax_rates_id = ?", "i", array( $product_info[0]['products_tax_class_id'] ) );

      $tax = $tax_query[0]['tax_rate'];
      $tax_desc = $tax_query[0]['tax_description'];
    } else {
      $tax = 0;
    }

    $attribute_price_sum = 0;
    $attribute_update = false;
    if ( sizeof( $attributes ) > 0 ) {
      $attribute_update = true;
      for ( $j=0; $j<sizeof( $attributes ); $j++ ) {
        $attribute_price_query = mysqli_prepared_query( "SELECT options_values_price, price_prefix FROM $this->tablename_a WHERE products_id = ? AND options_id = ? AND options_values_id = ?", "iii", array( $products_id, $attributes[$j][0], $attributes[$j][1] ) );
        $attribute_price = $attribute_price_query[0];
        if ( $attribute_price['price_prefix'] == '+' ) {
          $attribute_price_sum += (float)$attribute_price['options_values_price'];
        } else {
          $attribute_price_sum -= (float)$attribute_price['options_values_price'];
        }
        $attribute_name_query = mysqli_prepared_query( "SELECT products_options_name FROM $this->tablename_po WHERE products_options_id = ? AND language_id = ?", "ii", array( $attributes[$j][0], $languages_id ) );
        $attribute_name = $attribute_name_query[0];
        $options_name_query = mysqli_prepared_query( "SELECT products_options_values_name FROM $this->tablename_pov WHERE products_options_values_id = ? AND language_id = ?", "ii", array( $attributes[$j][1], $languages_id ) );
        $options_name = $options_name_query[0];

        $params = array( $orders_id, $attribute_name['products_options_name'], $options_name['products_options_values_name'], $attribute_price['options_values_price'], $attribute_price['price_prefix'] );

        mysqli_prepared_query( "INSERT INTO $this->tablename_opa (orders_id, orders_products_id, products_options, products_options_values, options_values_price, price_prefix) VALUES (?, 0, ?, ?, ?, ?)", "issds", $params );
      }
    }

    $special_price = mysqli_prepared_query( "SELECT specials_new_products_price
        FROM $this->tablename_s
        WHERE products_id = ?
        AND status = 1", "i", array( $products_id ) );

    if ( isset( $special_price[0]['specials_new_products_price'] ) ) {
      $final_price = (float)$special_price[0]['specials_new_products_price'] + (float)$attribute_price_sum;
    } else {
      $final_price = (float)$product_info[0]['products_price'] + (float)$attribute_price_sum;
    }


    $params = array( $orders_id, $products_id, $product_info[0]['products_model'], $product_info[0]['products_name'], $product_info[0]['products_price'], $final_price, $tax, $products_quantity );

    mysqli_prepared_query( "INSERT INTO $this->tablename_op (orders_id, products_id, products_model, products_name, products_price, final_price, products_tax, products_quantity) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", "iissdddi", $params );

    $orders_products_id = tep_db_insert_id();
    if ( $attribute_update == true ) {
      mysqli_prepared_query( "UPDATE $this->tablename_opa SET orders_products_id = ? WHERE orders_products_id = 0", "i", array( $orders_products_id ) );
    }

    #Update the Order Totals
    $this->tep_db_update_totals( $orders_id );

    #Update Product Totals if on 'Edit Order' Page
    if ( isset( $GET['edit_order'] ) ) {
      $products_total_html = $this->edit_order( $GET );
    } else {
      $products_total_html = $this->get_order( $GET, "all" );
    }

    #Send Success Message
    die( json_encode( array( 'Result' => 'OK', 'Message' => 'Product added.', 'products_total_html' => json_encode( $products_total_html ) ) ) );

  } # End Set Attributes


  /* Search a Product */
  public function search_product( $GET, $POST ) {
    global $languages_id;
    header( 'Content-Type: application/json' );

    $params = array( '%'.$GET['term'].'%', '%'.$GET['term'].'%', $languages_id );
    $products_query = mysqli_prepared_query( "SELECT distinct p.products_id, pd.products_name, p.products_model FROM $this->tablename_pd pd LEFT JOIN $this->tablename_p p ON (p.products_id = pd.products_id) WHERE (pd.products_name LIKE ? OR  p.products_model LIKE ?) AND  pd.language_id = ? AND p.products_status = '1' ORDER BY pd.products_name ASC LIMIT 20", "ssi", $params );

    if ( count( $products_query ) ) {
      $return_arr = array();
      foreach ( $products_query as $products ) {
        $row_array['id'] = 'data-pid=\'' .  $products['products_id'] . '\' data-pname=\'' .  $products['products_name'] . '\'';
        $row_array['value'] = $products['products_name'] . ( ( $products['products_model'] != '' ) ? ' (' . $products['products_model'] . ')' : '' );

        array_push( $return_arr, $row_array );
      }

    } else {
      $return_arr = array( 'value' => PRODUCTS_SEARCH_NO_RESULTS );
    }

    return json_encode( $return_arr );

  } # End Search Product


  /* Edit Order Page */
  public function edit_order( $GET ) {
    global $language, $languages_id, $currencies;

    include_once DIR_WS_LANGUAGES . $language . '/order_handler.php';

    if ( isset($order) && is_object($order) ) unset($order);

    include_once DIR_WS_CLASSES . 'order.php';
    $order = new order( $GET['oID'] );
    $cart  = new shoppingCart();
    $order->content_type = $cart->get_content_type();

    ob_start();
    include_once DIR_WS_MODULES . 'order_handler/edit_order.php';
    $output = ob_get_contents();
    ob_end_clean();

    return $output;

  } # End Edit Order Page


  public function mail_confirmation( $GET ) {
    global $language, $currencies;
    header( 'Content-Type: application/json' );

    include_once DIR_WS_CLASSES . 'order.php';
    $order = new order( (int)$GET['oID'] );
    $cart  = new shoppingCart();
    $order->content_type = $cart->get_content_type();

    $products_ordered = '';
    $order_totals     = '';

    #Error Handling
    if ( !is_numeric( $GET['oID'] ) || !is_numeric( $GET['cID'] ) || is_null( $order->customer['format_id'] ) ) {
      $message = "";
      if ( !is_numeric( $GET['oID'] ) ) $message .= "Not a numeric order number.<br>" . "\n";
      if ( !is_numeric( $GET['cID'] ) ) $message .= "Not a numeric customer ID.<br>" . "\n";
      if ( is_null( $order->customer['format_id'] ) ) $message .= "Order does not exist.";

      #HTTP/1.1 404 Not Found
      http_response_code( 404 );
      die( json_encode( array( 'Result' => 'ERROR', 'Message' => $message ) ) );
    }
    #END Error Handling


    for ( $i=0, $n=sizeof( $order->products ); $i<$n; $i++ ) {
      $products_ordered .= $order->products[$i]['qty'] . ' x ' . $order->products[$i]['name'] . ' (' . /*$order->products[$i]['model'] .*/ ') = ' . $currencies->display_price( $order->products[$i]['final_price'], $order->products[$i]['tax'], $order->products[$i]['qty'] )/* . $products_ordered_attributes*/ . "\n";
    }

    $email_footer = $this->get_payment_modules( $order->info['payment_method'] );

    #PHPMailer Class
    if ( isset( $GET['php_mailer'] ) ) {
      require_once DIR_WS_MODULES . 'order_handler/PHPMailer/PHPMailerAutoload.php';
      $mail = new PHPMailer;

      $mail->isSMTP();                          # Set mailer to use SMTP
      $mail->Host       = PHPMAILER_HOST;       # Specify main and backup SMTP servers
      $mail->SMTPAuth   = PHPMAILER_SMTP_AUTH;  # Enable SMTP authentication
      $mail->Username   = PHPMAILER_USERNAME;   # SMTP username
      $mail->Password   = PHPMAILER_PASSWORD;   # SMTP password
      $mail->SMTPSecure = PHPMAILER_SECURE;     # Enable encryption, 'ssl' also accepted
      $mail->CharSet    = PHPMAILER_CHARSET;    # Character Encoding

      $mail->From       = PHPMAILER_FROM;       # Sender E-Mail address
      $mail->FromName   = PHPMAILER_FROMNAME;   # Sender Name
    }


    # lets start with the email confirmation
    $email_order = STORE_NAME . "\n" .
      EMAIL_SEPARATOR . "\n" .
      EMAIL_TEXT_ORDER_NUMBER . ' ' . $GET['oID'] . "\n" .
      EMAIL_TEXT_INVOICE_URL . ' ' . tep_catalog_href_link( FILENAME_CATALOG_ACCOUNT_HISTORY_INFO, 'order_id=' . $GET['oID'], 'SSL', false ) . "\n" .
      EMAIL_TEXT_DATE_ORDERED . ' ' . strftime( DATE_FORMAT_LONG ) . "\n\n";
    if ( isset( $order->info['comments'] ) ) {
      $email_order .= tep_db_output( $order->info['comments'] ) . "\n\n";
    }
    $email_order .= EMAIL_TEXT_PRODUCTS . "\n" .
      EMAIL_SEPARATOR . "\n" .
      $products_ordered .
      EMAIL_SEPARATOR . "\n" .
      $order_totals . "\n";

    if ( $order->content_type != 'virtual' ) {
      $email_order .= "\n" . EMAIL_TEXT_DELIVERY_ADDRESS . "\n" .
        EMAIL_SEPARATOR . "\n" .
        tep_address_format( $order->customer['format_id'], $order->delivery, 0, '', "\n" ) . "\n" . "\n";
    }


    $email_order .= EMAIL_TEXT_PAYMENT_METHOD . "\n" .
      EMAIL_SEPARATOR . "\n";

    $email_order .= $order->info['payment_method'] . "\n\n";
    if ( isset( $email_footer['email_footer'] ) ) {
      $email_order .= $email_footer['email_footer'] . "\n\n";
    }


    if ( 'true' == SEND_EMAILS ) {
      #PHPMailer Class
      if ( isset( $GET['php_mailer'] ) ) {
        $mail->addAddress( $order->customer['email_address'], $order->customer['name'] );  # Add a recipient

        # send emails to other people
        if ( SEND_EXTRA_ORDER_EMAILS_TO != '' ) {
          $mail->addAddress( SEND_EXTRA_ORDER_EMAILS_TO, STORE_OWNER );  # Add a recipient
        }

        $mail->WordWrap = PHPMAILER_WORDWRAP;
        $mail->ContentType = PHPMAILER_CONTENTTYPE;
        $mail->isHTML( PHPMAILER_IS_HTML ); # Set email format to HTML


        $mail->Subject    = EMAIL_TEXT_SUBJECT . ' - ' . $GET['oID'];
        $mail->Body       = nl2br( $email_order );
        $mail->AltBody    = strip_tags( $email_order );

        if ( ! $mail->send() ) {
          http_response_code( 400 );
          die( 'Mailer Error: ' . $mail->ErrorInfo );
        }
        $mail->clearAddresses();
      } else {
        #PHP mail()
        tep_mail( $order->customer['name'], $order->customer['email_address'], EMAIL_TEXT_SUBJECT . ' - ' . $GET['oID'], $email_order, STORE_OWNER, STORE_OWNER_EMAIL_ADDRESS );

        #send emails to other people
        if ( SEND_EXTRA_ORDER_EMAILS_TO != '' ) {
          tep_mail( '', SEND_EXTRA_ORDER_EMAILS_TO, EMAIL_TEXT_SUBJECT . ' - ' . $GET['oID'], $email_order, STORE_OWNER, STORE_OWNER_EMAIL_ADDRESS );
        }
      }
    }


    $message = "Order Confirmation mailed to " . trim( $order->delivery['name'] ) . ".";

    die( json_encode( array( 'Result' => 'OK', 'Message' => $message ) ) );
  }

  public function delete_order( $POST ) {
    #SQL
    $sql_o           = "DELETE FROM $this->tablename_o   WHERE orders_id = ?";
    $sql_op          = "DELETE FROM $this->tablename_op  WHERE orders_id = ?";
    $sql_opa         = "DELETE FROM $this->tablename_opa WHERE orders_id = ?";
    $sql_osh         = "DELETE FROM $this->tablename_osh WHERE orders_id = ?";
    $sql_ot          = "DELETE FROM $this->tablename_ot  WHERE orders_id = ?";

    $sql_restock     = "SELECT products_id, products_quantity FROM $this->tablename_op WHERE orders_id = ?";
    $sql_restock_del = "UPDATE $this->tablename_p SET products_quantity = products_quantity + ?, products_ordered = products_ordered - ? WHERE products_id = ?";

    #Restock Products
    if ( "true" == $POST['restock'] ) {
      #Create the prepared statement
      $stmt_restock        = $this->link->prepare( $sql_restock );
      $stmt_restock_del    = $this->link->prepare( $sql_restock_del );
      #Bind parameters to the query
      $bp_restock          = $stmt_restock->bind_param( "i", $POST['orders_id'] );
      $bp_restock_del      = $stmt_restock_del->bind_param( "iii", $products_quantity, $products_quantity, $products_id );
      #Run the prepared statement
      $ex_restock          = $stmt_restock->execute();

      #Get the products info
      if ( true === function_exists( 'mysqli_stmt_get_result' ) ) {
        # With Mysqlnd
        $res_restock         = $stmt_restock->get_result();
        while ( $restock     = $res_restock->fetch_assoc() ) {
          $products_quantity = $restock['products_quantity'];
          $products_id       = $restock['products_id'];
          $ex_restock_del    = $stmt_restock_del->execute();
        }

        #Close open connections
        $stmt_restock->close();
      } else {
        # Without Mysqlnd
        $result              = mysqli_get_res( $stmt_restock );
        foreach ( $result as $restock ) {
          $products_quantity = $restock['products_quantity'];
          $products_id       = $restock['products_id'];
          $ex_restock_del    = $stmt_restock_del->execute();
        }
      }


      #Close open connections
      $stmt_restock_del->close();

      #Error Handling
      if ( false === $stmt_restock || false === $bp_restock || false === $ex_restock )
        $error = json_encode( array( 'Result' => 'ERROR', 'Message' => $sql_restock.' failed: ' . htmlspecialchars( $this->link->error ) ) );
      if ( false === $stmt_restock_del || false === $bp_restock_del || false === $ex_restock_del )
        $error = json_encode( array( 'Result' => 'ERROR', 'Message' => $sql_restock_del.' failed: ' . htmlspecialchars( $this->link->error ) ) );
    }
    #END Restock Products

    #Create the prepared statement
    $stmt_o         = $this->link->prepare( $sql_o );
    $stmt_op        = $this->link->prepare( $sql_op );
    $stmt_opa       = $this->link->prepare( $sql_opa );
    $stmt_osh       = $this->link->prepare( $sql_osh );
    $stmt_ot        = $this->link->prepare( $sql_ot );

    #Bind parameters to the query
    $bp_o           = $stmt_o->bind_param( "i", $POST['orders_id'] );
    $bp_op          = $stmt_op->bind_param( "i", $POST['orders_id'] );
    $bp_opa         = $stmt_opa->bind_param( "i", $POST['orders_id'] );
    $bp_osh         = $stmt_osh->bind_param( "i", $POST['orders_id'] );
    $bp_ot          = $stmt_ot->bind_param( "i", $POST['orders_id'] );

    #Run the prepared statement
    $ex_o           = $stmt_o->execute();
    $ex_op          = $stmt_op->execute();
    $ex_opa         = $stmt_opa->execute();
    $ex_osh         = $stmt_osh->execute();
    $ex_ot          = $stmt_ot->execute();

    #Close open connections
    $stmt_o->close();
    $stmt_op->close();
    $stmt_opa->close();
    $stmt_osh->close();
    $stmt_ot->close();


    #Error Handling
    $error = false;

    if ( false === $stmt_o || false === $bp_o || false === $ex_o )
      $error = json_encode( array( 'Result' => 'ERROR', 'Message' => $sql_o.' failed: ' . htmlspecialchars( $this->link->error ) ) );
    if ( false === $stmt_op || false === $bp_op || false === $ex_op )
      $error = json_encode( array( 'Result' => 'ERROR', 'Message' => $sql_op.' failed: ' . htmlspecialchars( $this->link->error ) ) );
    if ( false === $stmt_opa || false === $bp_opa || false === $ex_opa )
      $error = json_encode( array( 'Result' => 'ERROR', 'Message' => $sql_opa.' failed: ' . htmlspecialchars( $this->link->error ) ) );
    if ( false === $stmt_osh || false === $bp_osh || false === $ex_osh )
      $error = json_encode( array( 'Result' => 'ERROR', 'Message' => $sql_osh.' failed: ' . htmlspecialchars( $this->link->error ) ) );
    if ( false === $stmt_ot || false === $bp_ot || false === $ex_ot )
      $error = json_encode( array( 'Result' => 'ERROR', 'Message' => $sql_ot.' failed: ' . htmlspecialchars( $this->link->error ) ) );

    if ( false !== $error ) {
      #HTTP/1.1 400 Bad Request
      http_response_code( 400 );
      return $error;
    }

    return json_encode( array( 'Result' => 'OK' ) );
  } # End delete_order


  public function duplicate_order( $GET, $POST ) {
    global $currencies;
    header( 'Content-Type: application/json' );

    include_once DIR_WS_CLASSES . 'order.php';
    $GET['oID'] = (int)$GET['duplicate_order'];
    $order = new order( $GET['oID'] );


    #Error Handling
    if ( !is_numeric( $GET['oID'] ) || is_null( $order->customer['format_id'] ) ) {
      $message = "";
      if ( !is_numeric( $GET['oID'] ) ) $message .= "Not a numeric order number.<br>" . "\n";
      if ( is_null( $order->customer['format_id'] ) ) $message .= "Order does not exist.";

      #HTTP/1.1 404 Not Found
      http_response_code( 404 );
      die( json_encode( array( 'Result' => 'ERROR', 'Message' => $message ) ) );
    }

    #Start Order Duplication
    $sql_data_array =
      array(
      'customers_id'                => $order->customer['customers_id'],
      'customers_name'              => ( isset( $order->customer['name'] ) ? $order->customer['name'] : '' ),
      'customers_company'           => ( isset( $order->customer['company'] ) ? $order->customer['company'] : '' ),
      'customers_street_address'    => ( isset( $order->customer['street_address'] ) ? $order->customer['street_address'] : '' ),
      'customers_suburb'            => ( isset( $order->customer['suburb'] ) ? $order->customer['suburb'] : '' ),
      'customers_city'              => ( isset( $order->customer['city'] ) ? $order->customer['city'] : '' ),
      'customers_postcode'          => ( isset( $order->customer['postcode'] ) ? $order->customer['postcode'] : '' ),
      'customers_state'             => ( isset( $order->customer['state'] ) ? $order->customer['state'] : '' ),
      'customers_country'           => ( isset( $order->customer['country'] ) ? $order->customer['country'] : '' ),
      'customers_telephone'         => ( isset( $order->customer['telephone'] ) ? $order->customer['telephone'] : '' ),
      'customers_email_address'     => ( isset( $order->customer['email_address'] ) ? $order->customer['email_address'] : '' ),
      'customers_address_format_id' => ( isset( $order->customer['format_id'] ) ? $order->customer['format_id'] : '' ),
      'delivery_name'               => ( isset( $order->delivery['name'] ) ? trim( $order->delivery['name'] ) : '' ),
      'delivery_company'            => ( isset( $order->delivery['company'] ) ? $order->delivery['company'] : '' ),
      'delivery_street_address'     => ( isset( $order->delivery['street_address'] ) ? $order->delivery['street_address'] : '' ),
      'delivery_suburb'             => ( isset( $order->delivery['suburb'] ) ? $order->delivery['suburb'] : '' ),
      'delivery_city'               => ( isset( $order->delivery['city'] ) ? $order->delivery['city'] : '' ),
      'delivery_postcode'           => ( isset( $order->delivery['postcode'] ) ? $order->delivery['postcode'] : '' ),
      'delivery_state'              => ( isset( $order->delivery['state'] ) ? $order->delivery['state'] : '' ),
      'delivery_country'            => ( isset( $order->delivery['country'] ) ? $order->delivery['country'] : '' ),
      'delivery_address_format_id'  => ( isset( $order->delivery['format_id'] ) ? $order->delivery['format_id'] : '' ),
      'billing_name'                => ( isset( $order->billing['name'] ) ? $order->billing['name'] : '' ),
      'billing_company'             => ( isset( $order->billing['company'] ) ? $order->billing['company'] : '' ),
      'billing_street_address'      => ( isset( $order->billing['street_address'] ) ? $order->billing['street_address'] : '' ),
      'billing_suburb'              => ( isset( $order->billing['suburb'] ) ? $order->billing['suburb'] : '' ),
      'billing_city'                => ( isset( $order->billing['city'] ) ? $order->billing['city'] : '' ),
      'billing_postcode'            => ( isset( $order->billing['postcode'] ) ? $order->billing['postcode'] : '' ),
      'billing_state'               => ( isset( $order->billing['state'] ) ? $order->billing['state'] : '' ),
      'billing_country'             => ( isset( $order->billing['country'] ) ? $order->billing['country'] : '' ),
      'billing_address_format_id'   => ( isset( $order->billing['format_id'] ) ? $order->billing['format_id'] : '' ),
      'payment_method'              => ( isset( $order->info['payment_method'] ) ? $order->info['payment_method'] : '' ),
      'cc_type'                     => ( isset( $order->info['cc_type'] ) ? $order->info['cc_type'] : '' ),
      'cc_owner'                    => ( isset( $order->info['cc_owner'] ) ? $order->info['cc_owner'] : '' ),
      'cc_number'                   => ( isset( $order->info['cc_number'] ) ? $order->info['cc_number'] : '' ),
      'cc_expires'                  => ( isset( $order->info['cc_expires'] ) ? $order->info['cc_expires'] : '' ),
      'date_purchased'              => 'now()',
      'orders_status'               => '1',
      'currency'                    => ( isset( $order->info['currency'] ) ? $order->info['currency'] : '' ),
      'currency_value'              => ( isset( $order->info['currency_value'] ) ? $order->info['currency_value'] : '' ),
    );

    tep_db_prepared_perform( TABLE_ORDERS, $sql_data_array );

    $insert_id = tep_db_insert_id();
    for ( $i=0, $n=sizeof( $order->totals ); $i<$n; $i++ ) {
      $sql_data_array =
        array(
        'orders_id'  => $insert_id,
        'title'      => $order->totals[$i]['title'],
        'text'       => $order->totals[$i]['text'],
        'value'      => $order->totals[$i]['value'],
        'class'      => $order->totals[$i]['class'],
        'sort_order' => $i );
      tep_db_prepared_perform( TABLE_ORDERS_TOTAL, $sql_data_array );
    }

    $customer_notification = ( SEND_EMAILS == 'true' ) ? '1' : '0';
    $products_ordered = '';

    $sql_data_array =
      array(
      'orders_id'         => $insert_id,
      'orders_status_id'  => '1',
      'date_added'        => 'now()',
      'customer_notified' => $customer_notification,
    );

    tep_db_prepared_perform( TABLE_ORDERS_STATUS_HISTORY, $sql_data_array );

    for ( $i=0, $n=sizeof( $order->products ); $i<$n; $i++ ) {
      # Stock Update - Joao Correia
      if ( STOCK_LIMITED == 'true' ) {
        if ( DOWNLOAD_ENABLED == 'true' ) {
          $stock_query_raw = "SELECT products_quantity, pad.products_attributes_filename
            FROM $this->tablename_p p
            LEFT JOIN $this->tablename_a pa
            ON p.products_id=pa.products_id
            LEFT JOIN $this->tablename_pad pad
            ON pa.products_attributes_id=pad.products_attributes_id
            WHERE p.products_id = ?";
          $params[] = tep_get_prid( $order->products[$i]['id'] );
          $typeDef = 'i';
          # Will work with only one option for downloadable products
          # otherwise, we have to build the query dynamically with a loop
          $products_attributes = ( isset( $order->products[$i]['attributes'] ) ) ? $order->products[$i]['attributes'] : '';
          if ( is_array( $products_attributes ) ) {
            $stock_query_raw .= " AND pa.options_id = ? AND pa.options_values_id = '" . (int)$products_attributes[0]['value_id'] . "'";
            $params[]         = $products_attributes[0]['option_id'];
            $typeDef         .= 'i';
          }
          $stock_query = mysqli_prepared_query( $stock_query_raw, $typeDef, $params );
        } else {
          $stock_query = mysqli_prepared_query( "SELECT products_quantity FROM $this->tablename_p WHERE products_id = ?", "i", array( tep_get_prid( $order->products[$i]['id'] ) ) );
        }
        if ( count( $stock_query ) > 0 ) {
          $stock_values = $stock_query[0];
          # do not decrement quantities if products_attributes_filename exists
          if ( ( DOWNLOAD_ENABLED != 'true' ) || ( !$stock_values['products_attributes_filename'] ) ) {
            $stock_left = $stock_values['products_quantity'] - $order->products[$i]['qty'];
          } else {
            $stock_left = $stock_values['products_quantity'];
          }
          mysqli_prepared_query( "UPDATE $this->tablename_p SET products_quantity = ? WHERE products_id = ?", "ii", array( $stock_left, tep_get_prid( $order->products[$i]['id'] ) ) );
          if ( ( $stock_left < 1 ) && ( STOCK_ALLOW_CHECKOUT == 'false' ) ) {
            mysqli_prepared_query( "UPDATE $this->tablename_p SET products_status = '0' WHERE products_id = ?", "i", array( tep_get_prid( $order->products[$i]['id'] ) ) );
          }
        }
      }

      # Update products_ordered (for bestsellers list)
      mysqli_prepared_query( "UPDATE $this->tablename_p SET products_ordered = products_ordered + ? WHERE products_id = ?", "ii", array( sprintf( '%d', $order->products[$i]['qty'] ), tep_get_prid( $order->products[$i]['id'] ) ) );

      $sql_data_array =
        array(
        'orders_id'         => $insert_id,
        'products_id'       => tep_get_prid( $order->products[$i]['id'] ),
        'products_model'  => $order->products[$i]['model'],
        'products_name'     => $order->products[$i]['name'],
        'products_price'    => $order->products[$i]['price'],
        'final_price'       => $order->products[$i]['final_price'],
        'products_tax'      => $order->products[$i]['tax'],
        'products_quantity' => $order->products[$i]['qty'],
      );
      tep_db_prepared_perform( TABLE_ORDERS_PRODUCTS, $sql_data_array );
      $order_products_id = tep_db_insert_id();

      #------insert customer choosen option to order--------
      $attributes_exist = '0';
      $products_ordered_attributes = '';
      if ( isset( $order->products[$i]['attributes'] ) ) {
        $attributes_exist = '1';
        for ( $j=0, $n2=sizeof( $order->products[$i]['attributes'] ); $j<$n2; $j++ ) {

          $sql_data_array =
            array(
            'orders_id'               => $insert_id,
            'orders_products_id'      => $order_products_id,
            'products_options'        => $order->products[$i]['attributes'][$j]['option'],
            'products_options_values' => $order->products[$i]['attributes'][$j]['value'],
            'options_values_price'    => $order->products[$i]['attributes'][$j]['price'],
            'price_prefix'            => $order->products[$i]['attributes'][$j]['prefix'],
          );
          tep_db_prepared_perform( TABLE_ORDERS_PRODUCTS_ATTRIBUTES, $sql_data_array );

          if ( ( DOWNLOAD_ENABLED == 'true' ) && isset( $attributes_values['products_attributes_filename'] ) && tep_not_null( $attributes_values['products_attributes_filename'] ) ) {
            $sql_data_array =
              array(
              'orders_id'                => $insert_id,
              'orders_products_id'       => $order_products_id,
              'orders_products_filename' => $attributes_values['products_attributes_filename'],
              'download_maxdays'         => $attributes_values['products_attributes_maxdays'],
              'download_count'           => $attributes_values['products_attributes_maxcount'],
            );
            tep_db_prepared_perform( TABLE_ORDERS_PRODUCTS_DOWNLOAD, $sql_data_array );
          }
          $products_ordered_attributes .= "\n\t" . $order->products[$i]['attributes'][$j]['option'] . ' ' . $order->products[$i]['attributes'][$j]['value'];
        }
      }

      #------insert customer choosen option eof ----
      $products_ordered .= $order->products[$i]['qty'] . ' x ' . $order->products[$i]['name'] . ' (' . $order->products[$i]['model'] . ') = ' . $currencies->display_price( $order->products[$i]['final_price'], $order->products[$i]['tax'], $order->products[$i]['qty'] ) . $products_ordered_attributes . "\n";
    }


    $message = "New order created for customer " . trim( $order->delivery['name'] ) . " with order number " . $insert_id . ".";

    return $this->get_orders( $GET, $POST, $insert_id, $message );
  }

  public function get_orders( $GET, $POST, $orders_id = '', $message = '' ) {
    global $languages_id, $language;
    header( 'Content-Type: application/json' );

    require_once DIR_WS_MODULES . 'order_handler/general_functions.php';
    include_once DIR_WS_LANGUAGES . $language . '/order_handler.php';

    # Configure Sorting Options
    $page_limit = ( isset( $GET['jtPageSize'] ) && is_numeric( $GET['jtPageSize'] ) ? 'LIMIT ' . $GET['jtStartIndex'] . ', ' . $GET['jtPageSize'] : 'LIMIT 0, 10' );
    $order_by   = ( isset( $GET['jtSorting'] ) ? ''.$GET['jtSorting'] : 'o.orders_id ASC' );

    $params     = array( $languages_id );
    $typeDef    = "i";
    $specific   = "";
    $status     = "";

    if ( is_numeric( $orders_id ) ) {
      array_push( $params, $orders_id );
      $typeDef .= "i";
      $specific = "AND o.orders_id = ?";
    } elseif ( is_array( $orders_id ) ) {
      array_push( $params, $orders_id[0] );
      $typeDef .= "i";
      $specific = "AND o.orders_id > ? AND o.orders_status = 1";
    }

    /* Order Selection Query */

    # Sort by Search
    if ( isset( $POST['search'] ) && !empty( $POST['search'] ) ) {
      array_push( $params, '%'.$POST['search'].'%', '%'.$POST['search'].'%' );
      $typeDef  .= "ss";
      $specific  = "AND (o.customers_name LIKE ? OR o.customers_email_address LIKE ?";

      if ( is_numeric( $POST['search'] ) ) {
        array_push( $params, (int)$POST['search'], (int)$POST['search'] );
        $specific .= " OR o.customers_id = ? OR o.orders_id = ?)";
        $typeDef  .= "ii";
      } else {
        $specific .= ")";
      }

    # Sort By Status
    } elseif ( isset( $POST['status'] ) && is_numeric( $POST['status'] ) ) {
      array_push( $params, $POST['status'] );
      $typeDef         .= "i";
      $status           = "AND o.orders_status = ?";
      $status_row_query = "WHERE o.orders_status = ?";
      $status_row_param = $POST['status'];
    }

    # SQL
    $orders_query_raw = "SELECT o.orders_id, o.customers_id, o.customers_name, o.customers_company, o.delivery_street_address, o.delivery_suburb, o.delivery_city, o.delivery_postcode, o.delivery_state, o.delivery_country, o.customers_telephone, o.customers_email_address, o.date_purchased, o.orders_status, o.payment_method, MIN(osh.comments) as comments, ot.text AS order_total, (SELECT title FROM orders_total WHERE orders_id = o.orders_id AND class = 'ot_shipping') as shipping_method, (SELECT orders_status_name FROM orders_status WHERE orders_status_id = o.orders_status AND language_id = ?) as orders_status_name, (SELECT SUM(products_quantity) FROM orders_products WHERE orders_id = o.orders_id GROUP BY orders_id) as products_quantity FROM orders o JOIN orders_status_history osh USING (orders_id) JOIN orders_total ot USING (orders_id) WHERE ot.class = 'ot_total' $status $specific GROUP BY orders_id ORDER BY $order_by $page_limit";

    #Get Orders
    $orders_query = mysqli_prepared_query( $orders_query_raw, $typeDef, $params );

    #Count Rows
    $specific   = ! empty( $specific ) ? "WHERE " . substr_replace( $specific, "", 0, 4 ) : '';
    $status     = ! empty( $status ) ? "WHERE " . substr_replace( $status, "", 0, 4 ) : '';
    $typeDef    = substr_replace( $typeDef, "", 0, 1 );
    array_shift( $params );

    $num_rows = mysqli_prepared_query( "SELECT count(*) as total FROM orders o $status $specific", $typeDef, $params );

    $orders_statuses = array();
    $envelope_back   = array();
    $orders_status_array = array();
    $orders_status_query = mysqli_prepared_query("SELECT orders_status_id, orders_status_name FROM $this->tablename_os WHERE language_id = ? ORDER BY orders_status_id ASC", "i", array($languages_id));
    foreach ($orders_status_query as $orders_status) {
      $orders_statuses[] = array('id' => $orders_status['orders_status_id'],
       'text' => $orders_status['orders_status_name']);
      $orders_status_array[$orders_status['orders_status_id']] = $orders_status['orders_status_name'];
    }

    #Create JSON Array
    for ( $i=0; $i < count( $orders_query ) ; $i++ ) {
      $orders_query[ $i ]['order']             = "<span class=\"expand_order ajax_disable tooltip_set glyphicon glyphicon-resize-small\" data-placement=\"bottom\" data-tooltip-title=\"" . TOOLTIP_ACTION_FIELD_TITLE . "\" . title=\"" . TOOLTIP_EXPAND_ORDER . "\" title=\"Edit Order\"></span>";

      $orders_query[ $i ]['customers_id']      = $orders_query[ $i ]['customers_id'];

      $orders_query[ $i ]['comments']          = "<span class=\"comments tooltip_set\" data-placement=\"bottom\" title=\"" . $orders_query[ $i ]['comments'] . "\" title=\"" . $orders_query[ $i ]['comments'] . "\">" . $orders_query[ $i ]['comments'] . "</span>";

      # Shipping Method as Plain Text
      $orders_query[ $i ]['shipping_method_text'] = '<span id="shipping_method_text_' . $orders_query[ $i ]['orders_id'] . '">' . $orders_query[ $i ]['shipping_method'] . '</span>';

      # Create Shipping Method Dropdown
      $orders_query[ $i ]['shipping_method']      = '<span id="shipping_method_' . $orders_query[ $i ]['orders_id'] . '">' . tep_cfg_pull_down_shipping( $orders_query[ $i ]['shipping_method'], 'id="shipping_method_pull_down_' . $orders_query[ $i ]['orders_id'] . '" class="shipping_method_pull_down multiselect btn btn-xs btn-info" data-table="orders" data-field="shipping_method"', false ) . '</span>';

      # Payment Method as Plain Text
      $orders_query[ $i ]['payment_method_text'] = '<span id="payment_method_text_' . $orders_query[ $i ]['orders_id'] . '">' . str_replace( 'Posten AB - Postförskott (Inrikes)', 'Postförskott', $orders_query[ $i ]['payment_method'] ) . '</span>';

      # Create Payment Method Dropdown
      $orders_query[ $i ]['payment_method']      = '<span id="payment_method_' . $orders_query[ $i ]['orders_id'] . '">' . tep_cfg_pull_down_payment( $orders_query[ $i ]['payment_method'], 'id="payment_method_pull_down_' . $orders_query[ $i ]['orders_id'] . '" class="payment_method_pull_down multiselect btn btn-xs btn-info" data-table="orders" data-field="payment_method"', false ) . '</span>';

      $orders_query[ $i ]['edit_order']        = "<a class=\"edit_order tooltip_set\" data-placement=\"bottom\" title=\"" . TOOLTIP_EDIT_ORDERS . "\" title=\"" . TOOLTIP_EDIT_ORDERS . "\" href=\"" . tep_href_link( "get_table.php", 'oID=' . $orders_query[ $i ]['orders_id'] . '&action=edit_order' . '&cID=' . $orders_query[ $i ]['customers_id'] ) . "\"><span class=\"glyphicon glyphicon-pencil\"></span></a>";

      $orders_query[ $i ]['search_customer_orders'] = "<a class=\"search_customer_orders tooltip_set\" data-placement=\"bottom\" title=\"" . sprintf(TOOLTIP_SEARCH_CUSTOMER_ORDERS, $orders_query[ $i ]['customers_name']) . "\" title=\"" . sprintf(TOOLTIP_SEARCH_CUSTOMER_ORDERS, $orders_query[ $i ]['customers_name']) . "\" data-search=\"" . $orders_query[ $i ]['customers_id'] . "\" style=\"margin-right:10px;\" href=\"" . tep_href_link( "get_table.php", "oID=" . $orders_query[ $i ]['orders_id'] . "&cID=" . $orders_query[ $i ]['customers_id'] . "&action=get_orders" ) . "\">" . "<span class=\"fa fa-search\"></span>" . "</a>";

      $orders_query[ $i ]['mail_confirmation'] = "<a class=\"mail_confirmation tooltip_set\" data-placement=\"bottom\" title=\"" . TOOLTIP_MAIL_CONFIRMATION . "\" title=\"" . TOOLTIP_MAIL_CONFIRMATION . "\" style=\"margin-right:10px;\" href=\"" . tep_href_link( "get_table.php", "oID=" . $orders_query[ $i ]['orders_id'] . "&cID=" . $orders_query[ $i ]['customers_id'] . "&action=mail_confirmation" ) . "\">" . "<span class=\"fa fa-envelope\"></span>" . "</a>";

      $orders_query[ $i ]['duplicate_order']   = "<a class=\"duplicate_order tooltip_set\" data-placement=\"bottom\" title=\"" . TOOLTIP_DUPLICATE_ORDER . "\" title=\"" . TOOLTIP_DUPLICATE_ORDER . "\" style=\"margin-right:10px;\" href=\"" . tep_href_link( "get_table.php", 'duplicate_order=' . $orders_query[ $i ]['orders_id'] . '&cID=' . $orders_query[ $i ]['customers_id'] . '&action=duplicate_order' ) . "\">" . "<span class=\"fa fa-files-o\"></span>" . "</a>";

      $orders_query[ $i ]['mail_customer']     = "<a class=\"mail_customer tooltip_set ajax_disable jtable-command-column\" data-placement=\"bottom\" title=\"" . ENTRY_MAIL_CUSTOMER . "\" title=\"" . ENTRY_MAIL_CUSTOMER . "\" style=\"margin-right:10px;\" href=\"" . tep_href_link( "contact.php", "oID=" . $orders_query[ $i ]['orders_id'] . "&customers_name=" . $orders_query[ $i ]['customers_name']  . "&customers_email_address=" . $orders_query[ $i ]['customers_email_address']  . "&cID=" . $orders_query[ $i ]['customers_id'] ) . "\">" . "<span class=\"fa fa-mail-reply\"></span>" . "</a>";

      $orders_query[ $i ]['orders_status']     = tep_draw_pull_down_menu('orders_status', $orders_statuses, $orders_query[ $i ]['orders_status'], 'class="orders_status"');

      $orders_query[ $i ]['orders_status_text']     = $orders_query[ $i ]['orders_status_name'];

    }

    $result =
      array(
      "Result"            => "OK",
      "Records"           => $orders_query,
      "TotalRecordCount"  => $num_rows[0]['total'],
    );
    if ( ! empty( $message ) ) $result['Message'] = $message;

    if ( ! tep_not_null( $orders_query ) ) {
      $sql = "SELECT max(orders_id) as orders_id FROM $this->tablename_o";
      $last_orders_id = tep_db_fetch_all( $sql );
      $result['LastOrderNumber'] = $last_orders_id[0]['orders_id'];
    }

    return json_encode( $result );
  } # End get_orders()


  public function get_order( $GET, $otField = false ) {
    global $languages_id, $currencies, $order, $cart;
    header( 'Content-Type: application/json' );

    include_once DIR_WS_CLASSES . 'order.php';
    $order = new order( $GET['oID'] );

    if ( !isset( $order->content_type ) ) {
      if ( !is_object( $cart ) ) $cart  = new shoppingCart();
      $order->content_type = $cart->get_content_type();
    }

    # Set Customers Country and Zone
    if ( !isset( $order->delivery['zone_id']) ) {
      $order_query = mysqli_prepared_query( "SELECT currency, countries_id, delivery_address_format_id, delivery_country, countries_iso_code_2, countries_iso_code_3 FROM $this->tablename_o o JOIN $this->tablename_co c ON (c.countries_name = o.delivery_country) WHERE orders_id = ? LIMIT 1", "i", array( $GET['oID'] ) );

      $order->delivery['country'] =
      array(
        'id'         => $order_query[0]['countries_id'],
        'title'      => $order_query[0]['delivery_country'],
        'iso_code_2' => $order_query[0]['countries_iso_code_2'],
        'iso_code_3' => $order_query[0]['countries_iso_code_3'],
      );

      $order->delivery['zone_id'] = $order_query[0]['delivery_address_format_id'];
      $currency = $order_query[0]['currency'];
    }

    if ( !is_numeric( $GET['oID'] ) || is_null( $order->customer['format_id'] ) ) {
      $message = "";
      if ( !is_numeric( $GET['oID'] ) ) $message .= "Not a numeric order number.<br>" . "\n";
      if ( is_null( $order->customer['format_id'] ) ) $message .= "Order does not exist.";
      #HTTP/1.1 404 Not Found
      http_response_code( 404 );
      die( json_encode( array( 'Result' => 'ERROR', 'Message' => $message ) ) );
    }

    if ( false === $otField || "all" == $otField ) {
      $retOrder[0]['orders_id'] = $GET['oID'];

      $retOrder[0]['customer_address'] = tep_editable_address( $order->customer['format_id'], $order->customer, 1, '', '<br>', 'customers_' );

      $retOrder[0]['shipping_address'] = tep_editable_address( $order->delivery['format_id'], $order->delivery, 1, '', '<br>', 'delivery_' );

      $retOrder[0]['billing_address']  = tep_editable_address( $order->billing['format_id'], $order->billing, 1, '', '<br>', 'billing_' );

      $retOrder[0]['email_address']    = '<span id="customers_email_address" data-table="orders" data-field="customers_email_address" class="ajaxLink">' . $order->customer['email_address'] . '</span>';

      $retOrder[0]['telephone']        = '<span id="customers_telephone" data-table="orders" data-field="customers_telephone" class="ajaxLink">' . ( ( isset( $order->customer['telephone'] ) && $order->customer['telephone'] != '' ) ? $order->customer['telephone'] : '___' ) . '</span>';


      # Create Shipping Method Dropdown
      $order_shipping_query = mysqli_prepared_query( "SELECT title FROM $this->tablename_ot WHERE orders_id = ? AND class = 'ot_shipping'", "i", array( $GET['oID'] ) );

      # Continue if Shipping exists
      if ( count( $order_shipping_query ) ) {
        # Get all available Shipping Quotes
        $quote = $this->get_shipping_modules( $GET['oID'] );

        # Loop Through Quotes and Search for Shipping Title
        for ( $i=0, $n=sizeof( $quote ); $i<$n; $i++ ) {

          $haystack = preg_replace( '/\d+/', '', $order_shipping_query[0]['title'] );
          $needle   = preg_replace( '/\d+/', '', $quote[$i]->quotes['methods'][0]['title'] );
          $extra    = ( isset( $quote[$i]->quotes['methods'][0]['module'] ) ? $quote[$i]->quotes['methods'][0]['module'] : $quote[$i]->title );

          if ( stristr(  $haystack, $needle ) || stristr( $haystack, $extra ) ) {
            $selected_shipping_module = $quote[$i]->title;
            break;
          }
        }
      }

      $retOrder[0]['shipping_method']  = tep_cfg_pull_down_shipping( $selected_shipping_module, 'id="shipping_method_pull_down" class="multiselect btn btn-xs btn-info" data-table="orders" data-field="shipping_method"', '' );

      # Create Payment Method Dropdown
      $custom = false;
      $payment = $this->get_payment_modules( $order->info['payment_method'] );
      if ( false == $payment['code'] ) $custom = true;

      $retOrder[0]['payment_method']   = tep_cfg_pull_down_payment( $order->info['payment_method'], 'id="payment_method_pull_down" class="multiselect btn btn-xs btn-info" data-table="orders" data-field="payment_method"', $custom );
    }

    include_once DIR_WS_MODULES . 'order_handler/order_totals_and_comments.php';

    if ( true === $otField || "all" == $otField )
      return $output;


    $output .= '</table>';

    $retOrder[0]['products']        = $output;

    $result = array( "Result" => "OK", "Records" => $retOrder );
    return json_encode( $result );
  } # End get_order()

} # End Class

______________________________________________________

 

* The End *

______________________________________________________

 

 

 

Note also that this Update will work without MySQL Native Driver (mysqlnd) installed.

 

 

Send a reply if you run into any problems.

Link to comment
Share on other sites

Hello,

 

I'm trying to install this contribution on a oscommerce 2.3.4 and I get this error when I access to order handler

 

Parse error: syntax error, unexpected T_FUNCTION in /directory/admin/includes/modules/order_handler/ajax_update.php on line 1541

 

This is the code

    # finally.. sort the f*ckers and then we're done
    usort( $order_totals, function( $a, $b ) {
        return $a['sort_order'] - $b['sort_order'];
      } );

    # ... and back again. What the hell happened?
    chdir( $dir );

Thanks in advance :)

Edited by PiLLaO
Link to comment
Share on other sites

Hello,

 

I'm trying to install this contribution on a oscommerce 2.3.4 and I get this error when I access to order handler

 

Parse error: syntax error, unexpected T_FUNCTION in /directory/admin/includes/modules/order_handler/ajax_update.php on line 1541

 

This is the code

    # finally.. sort the f*ckers and then we're done
    usort( $order_totals, function( $a, $b ) {
        return $a['sort_order'] - $b['sort_order'];
      } );

    # ... and back again. What the hell happened?
    chdir( $dir );

Thanks in advance :)

 

First of all, I'm a bit drunk right now..

 

Hello PiLLaO, is this the first time you install the order handler? Which "base" or "version" are you using? (E.g. code from the instructions or code from the previous  five posts?)

 

It's a real mess uploading updated packages to an Add-On here (on osC website), the founders should really get their thumb out of their (assumed) fat asses and update both the forum and the Add-On section.

I mean, how hard could it be? Could you pleeeease change the f-cking altavista style Add-On search algorithm? The algorithm used now has to have some kind of record in just making people angry and confused... (I mean, I don't want to be rude, but sometimes I get the feeling it's hiding the actual relevant results from me.... Of course the whole problem can and probably are in some degree attributed to me being an idiot but I think it's not all my fault, this time... )

 

After all these hours and beautiful ideas which finally culminated in osCommerce 2.2 & 2.3, a really good E-commerce solution in 2003 (estimated..) to get to this point but now, when, well, nobody really knows if their is any strategy for development? Seems more like patch the really awkward problems so at least it looks like it's made for this decade.

 

(Could we please just either scrap the 3.0 thing or put it through a newbie steel bath until it's easy enough for any idiot to understand and, of course, all the shitty modules already made within the community can be more or less compatible? The situation today seems be advancing to a "let's keep what we have and please don't stir the f-cking pot we have here, please, version 3 is soon to be released anyway so let's keep the really old code as long as it doesn't create any awkward deprecation notices. We don't want to cause any stir!

Better that the commercial enterprises continue with the development of E-commerce, they really did a great job this far..

 

It's almost like the osC community circus police (someone is directing this circus, open source or not) want to make  osC and Microsoft IE get in pace with each other. Let's wait for that or at least just wait (waiting is the important part)....

And I guess meanwhile MS get their act together and finish up their law suits and everything we could encourage some opportunists to fork some more E-commerce platforms out of oscommerce. Since they already are, well how many forks are there really?

 

This is Okay of course (I mean 9/10 shops look like shit anyway..), but I don't like the current situation where companies has to buy subscription/commitment plans to get their E-commerce business realized and all of them look more or less the same. This will, eventually, and at some point make the web become what it was 1999 with every shop looking almost the same as the next one. No Ajax, because we have to think of the poor suckers (who still uses IE 5/6/X ?), so let's reload the page over and over again. Waste is the new thing, right?

Better to keep it as it's always been, completely unintelligible layout with a lot of text (In Arial of course..). Banks are a good example, let them lead the way. Really amazing that you sometimes, somehow, in some way can get an pdf out of their system nowadays (only the cutting edge banks of course). Fantastic, they really lead the way and take advantage of all the new possibilities. I Haven't seen one single bank with a intuitive interface yet.. Looks to me like they are afraid of "new" things.

 

Yuk! If the OsCommerce team decided to roll up their sleeves, inject some psychotropics ( ...if needed to get thing rolling... ), trash the 3.0 thing completely, then begin with a new 3.0 thing which would be somewhat backwards compatible but still "somewhat" respectable code wise (I mean it's PHP, it will always be a spit on by those people who think they know something but can't sell anything..)..

 

Make sure osCommerce comes out green on every row on this page: http://en.wikipedia.org/wiki/Comparison_of_shopping_cart_software

It would be so great if osCommerce once again could lead the way of how future webshop will be formed so we don't have to have the "10-year-vacuum" again when everything look equally bad with the only difference that you can find more ugly pages with Google.

 

Obviously, I have sobered up enough now to regret this post, but I have stayed awake too long because of this stupidity to not post it.

Point is that I still think osCommerce can become #1 again and it doesn't take much to get it there and I base this on how crappy the average webshop is. They still look like shit, all of them. No question about it..

They are also reaaaaly slow, incredibly slow... And where's the creativity? The artists finally came and created some nice artwork so our eyes don't necessary have to bleed anymore of all animated gifs and flashing images or dancing Christmas trees and some unnamed hero put an end (almost) to the PopUp hysteria. The last years have been more about removing bad ideas than introducing any new ones. Mobile/Responsive pages is new of course...

 

Got to love the ones that still use some kind of Flash to show a spinning gnome or something on their holiday offer, real refreshing.. Also quite depressing to see banks that require some weird 64-bit Java application or similar for authentication (like that would make them more secure..)

 

Holy something my head hurts now, why didn't I go to sleep instead..

Summa summarum, I'm guilty of not following any of the osc development threads and of not contributing to any of them. I just hate how bad the internet still is after all these years and now even with people with actual education still doing equally bad websites.

 

I want oscommerce to be more of a flag ship again but the community (forums and website) have to get some kind of makeover. Searching Add-Ons must be fixed, it's really terrible now, feels like getting trapped in Soviet bureaucracy...

 

Add-Ons Statistics? Behold, the most popular Add-On as of 2011....

http://addons.oscommerce.com/stats

 

osC 3 should have everything included from the Wikipedia list and this is something I can contribute somewhat to. Got to love giving desirable features for free, right? B) :thumbsup:

 

Let the people who use IE <9 die in their loneliness. We can't wait for those people forever, it's their choice to use a browser which doesn't support the internet standards from this millennia. The Viruses will probably kill their computer eventually anyway, so problem will solve itself eventually..

 

Well, this is going to be a terrible day for Dr. Rolex :x

My only hope is to find someone with an even worse condition than myself, expectations very slim....

Link to comment
Share on other sites

Sorry, I forgot to say you more information, i hope you get feeling better today :thumbsup:

 

I use a fresh install of oscommerce 2.3.4 and your contribution:

Advanced Order Handler Rev3 - Compatible Without mysqlnd *NO SCREENSHOTS* (the last one)

 

I was reading and I understand that it was the correct file to install this contribution.

 

Sorry for my poor english o:)

Thanks you in advance

Link to comment
Share on other sites

Hi again,

 

Fresh install of osc 2.3.4, and drop on top with:

jQuery/Ajax Advanced Order Handler Rev3 for osCommerce 2.3.3 & 2.3.4 shopperPS 8 Jul 2014  

 

Don't show orders to edit

 

With drop on top of:

Advanced Order Handler Rev3 - Compatible Without mysqlnd *NO SCREENSHOTS* shopperPS 10 Sep 2014

 

Parse error: syntax error, unexpected T_FUNCTION in /home/admin/includes/modules/order_handler/ajax_update.php on line 1541

 

Thanks you in advance

Link to comment
Share on other sites

The parse error is caused by running an old version of PHP. You need at least PHP 5.3.0 to run this code. Talk to your host about upgrading to a modern version of PHP.

 

Your other problem may also be due to your old PHP version. It's hard to tell without more information.

 

Regards

Jim

See my profile for a list of my addons and ways to get support.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...