tested and updated annuaire and activation
authorKilian Saffran <ksaffran@dks.lu>
Tue, 25 Jun 2019 08:50:40 +0000 (10:50 +0200)
committerKilian Saffran <ksaffran@dks.lu>
Tue, 25 Jun 2019 08:50:40 +0000 (10:50 +0200)
13 files changed:
backoffice/api/lib/dksapp.pm
backoffice/api/lib/dksinvoice.pm
backoffice/api/process.cgi
backoffice/css/module.css
backoffice/index.cgi
backoffice/tmpl/module/annuaire/index.js
backoffice/tmpl/module/annuaire/index.tt
backoffice/tmpl/module/applications/index.js
backoffice/tmpl/module/applications/index.tt
backoffice/tmpl/module/applications/javascript.tt
backoffice/tmpl/module/js/modules_global.js
backoffice/vendors/js-form-validator/js-form-validator.js [new file with mode: 0644]
backoffice/vendors/js-form-validator/js-form-validator.min.js [new file with mode: 0644]

index b70a61a..6d94cb3 100644 (file)
@@ -7,6 +7,7 @@ use lib ('./lib');
 use lib ('./');
 use File::Basename;
 use Getopt::Long;
+use Data::Dumper;
 use dksdb;
 
 sub new {
@@ -22,31 +23,36 @@ sub activate_app(){
   my $iduser = shift;
   my $duration = shift;
   my $statusdate = shift;
-  my $apacc = $self->{db}->dbquery("select ac.id,ac.expiration,ap.app,app.inittables from appaccess ac join apps ap on (ac.id_app=ap.id) where ac.id_user=".$iduser." and id_app=".$idapp.";");
+  
+  
+  my $apacc = $self->{db}->dbquery("select ac.id,ac.expiration,ap.app,ap.inittables from appaccess ac join apps ap on (ac.id_app=ap.id) where ac.id_user=".$iduser." and id_app=".$idapp.";");
   my $res;
   if ($apacc){
     $res = $self->{db}->dbexec("update appaccess set expiration=date(case when expiration >= date('".$statusdate."') then expiration + interval '".$duration." month' else date('".$statusdate."') + interval '".$duration." month' end)  where id=".$apacc->{id});
   } else {
     $res = $self->{db}->dbexec("INSERT INTO appaccess (id_user, id_app, publicenabled, expiration) VALUES(".$iduser.", ".$idapp.", null, date('".$statusdate."') + interval '".$duration." month');");
-    # if (($apacc->{inittables}) && ($apacc->{inittables} ne "")){
-    #   my @tbl = split(",",$apacc->{inittables});
-    #   foreach my $t (@tbl){
-    #     
-    #   }
-    # }
-    if ($idapp eq "2"){ #is annauire
+  }
+  if ($idapp eq "2"){ #is annuaire
+      
       my $ud = $self->{db}->dbquery("SELECT * from users where id=".$iduser.";");
+      
       if (exists($ud->{id})){
-        $self->{db}->dbexec("INSERT INTO lawyercatalog (id_user)  VALUES (".$iduser.");");
-        my $profile_link = lc(substr($ud->{username},0,index($ud->{username},0,'@')));
-        my $profexists = $self->{db}->dbquery("SELECT COUNT(*)+1 as cnt from lawyercatalog where profile_link='".$profile_link."';");
-        if ($profexists->{cnt} ne "1"){
-          $profile_link .= $profexists->{cnt};
+        my $lc = $self->{db}->dbquery("select count(*) as cnt from lawyercatalog where id_user=".$iduser.";");
+        
+        if ($lc->{cnt} eq "0"){
+     
+          $self->{db}->dbexec("INSERT INTO lawyercatalog (id_user)  VALUES (".$iduser.");");
+          my $profile_link = lc(substr($ud->{username},0,index($ud->{username},'@')));
+          my $profexists = $self->{db}->dbquery("SELECT COUNT(*)+1 as cnt from lawyercatalog where profile_link='".$profile_link."';");
+          if ($profexists->{cnt} ne "1"){
+            $profile_link .= $profexists->{cnt};
+          }
+     
+          $self->{db}->dbexec("UPDATE lawyercatalog SET profile_link='".$profile_link."' WHERE id_user=".$iduser.";");
         }
-        $self->{db}->dbexec("UPDATE lawyercatalog SET profilelink='".$profile_link."' WHERE id_user=".$iduser.";");
       }
     }
-  }
+
   return $res;
 }
 
index fab7046..1c3331a 100644 (file)
@@ -6,7 +6,7 @@ use lib ('./lib');
 use lib ('./');
 use POSIX qw(strftime);
 use File::Basename;
-
+use Data::Dumper;
 use dksdb;
 use dksreport;
 use dksapp;
@@ -55,7 +55,7 @@ sub newinvoice(){
   if (exists($data->{paypal})){
     ($inv,$prd) = $self->getpaypaldetails($data);
     $inv->{invoices_payementmethod} = 'paypal';
-    $inv->{invoices_tranactiondata} = JSON::PP::encode_json($data);
+    $inv->{invoices_transactiondata} = JSON::PP::encode_json($data);
   } else {
     $inv->{invoices_email} = $data->{email};
     $inv->{invoices_recipient} = $data->{recipient};
@@ -128,7 +128,7 @@ sub getpaypaldetails(){
   my $self = shift;
   my $ppa = shift;
   my $adrdata->{invoices_email} = $ppa->{payer}->{email_address};
-  $adrdata->{invoices_recepient} = $ppa->{payer}->{name}->{givenname}." ".$ppa->{payer}->{name}->{surname};
+  $adrdata->{invoices_recipient} = $ppa->{payer}->{name}->{givenname}." ".$ppa->{payer}->{name}->{surname};
   $adrdata->{invoices_address} = $ppa->{shipping}->{address}->{address_line_1};
   $adrdata->{invoices_zip} = $ppa->{shipping}->{address}->{postal_code};
   $adrdata->{invoices_city} = $ppa->{shipping}->{address}->{admin_area_2};
index 185c5b0..75fa0bb 100644 (file)
@@ -65,24 +65,23 @@ if (($cgi->request_method() eq "GET") || ($cgi->request_method() eq "POST")){
       my $appinfo =  JSON::PP::decode_json($p->{appinfo});
       my $invdata = JSON::PP::decode_json($p->{invoice});
       my ($invid,$invstatus,$invamount,$invemail) = $inv->newinvoice($appinfo->{id_user},$invdata,$appinfo);
+
       if ($invid){
         $html->{result}= $invid;
         my $pdf = $inv->createpdf($invid,'invoice_dks','fr',dirname($ENV{"SCRIPT_FILENAME"}).'/reports');
+   
         if (-e dirname($ENV{"SCRIPT_FILENAME"}).'/reports/'.$pdf){
           if (($invstatus eq "PAYED") && ($invamount > 0)){
             my $eml = sendemail->new();
             $eml->sendemail('user_invoice',$sess->{id},$invemail,{},dirname($ENV{"SCRIPT_FILENAME"}).'/reports/'.$pdf);
           }
-        } else {
-          # open (LOG,">>tmp/app.log");
-          # print LOG "FILE: ".dirname($ENV{"SCRIPT_FILENAME"}).'/reports/'.$pdf." does not exist!\n";
-          # close(LOG);
-        }
+        } 
       }
       if (($invstatus ) && ($invstatus eq "PAYED")){
-        my $app = dksapp->new();
+       
         $inv->activate_app_byinvoiceid($invid);
       }
+      
     }
 
     elsif ($p->{fn} eq "savefield"){
@@ -216,8 +215,8 @@ if (($cgi->request_method() eq "GET") || ($cgi->request_method() eq "POST")){
         }
       }
       if ($invstatus == "PAYED"){
-        my $app = dksapp->new();
-        $app->activateapp($invid);
+        #my $app = dksapp->new();
+        $inv->activate_app_byinvoiceid($invid);
       }
       $html->{result} ="OK";
     }
@@ -226,12 +225,12 @@ if (($cgi->request_method() eq "GET") || ($cgi->request_method() eq "POST")){
     #   my $rep = report->new();
     #   $rep->generatepdf($invid,'invoices','invoicedata','pdfname','invoice_dks','fr',dirname($ENV{"SCRIPT_FILENAME"}).'/reports');
     # }
-    elsif($p->{fn} eq "getevents"){
-      my $sql = "select * from geteventsbydisponibility(".$sess->{id}.",date('".$p->{startdate}."'),date('".$p->{enddate}."')) order by startdate,starttime;";
-      my $res = $db->dbquerysorted($sql);
-      $html->{result}->{sql} = $sql;
-      $html->{result}->{events} = $res;
-    }
+    elsif($p->{fn} eq "getevents"){
+      my $sql = "select * from geteventsbydisponibility(".$sess->{id}.",date('".$p->{startdate}."'),date('".$p->{enddate}."')) order by startdate,starttime;";
+      my $res = $db->dbquerysorted($sql);
+      $html->{result}->{sql} = $sql;
+      $html->{result}->{events} = $res;
+    }
   }
   
 }
index 26ef47a..b463d2e 100644 (file)
@@ -13,4 +13,35 @@ img.preview {
   border: 1px solid #ced4da;
   border-radius: .25rem;
   padding: .375rem .375rem;
-}
\ No newline at end of file
+}
+
+[data-type='validator-error'] {
+  display: none;
+  font-size: 11px;
+  position: absolute;
+  z-index: 10;
+  margin-top: 2px;
+  color: #ffffff;
+  background: rgb(200, 53, 53);
+  box-shadow: 0 6px 2px -2px rgba(211, 77, 77, 0.47);
+  padding: 4px 10px;
+  border-radius: 1px;
+  pointer-events: none;
+}
+
+[data-type='validator-error']:before {
+  display: none;
+  content: '';
+  width: 0;
+  height: 0;
+  top: -5px;
+  position: absolute;
+  left: 6px;
+  border-left: 5px solid rgba(0, 0, 0, 0);
+  border-right: 5px solid rgba(0, 0, 0, 0);
+  border-bottom: 5px solid #c83535;
+}
+
+form input[type='text'].error,input[type='email'].error, form input[type='password'].error, form textarea.error {
+  border: 1px solid #fa4531;
+}
index 06acaaa..2c7d8da 100644 (file)
@@ -23,7 +23,12 @@ my $vars = $sitecfg;
 
 $vars->{filepath} = substr($cgi->url({-absolute=>1}),length($vars->{basepath})+1); 
 $vars->{baseurl} = $cgi->url({-base=>1}).$vars->{basepath};
-$vars->{siteurl} = $cgi->url({-base=>1}).dirname($vars->{basepath});
+if ($vars->{basepath} eq "/"){
+  $vars->{siteurl} = $cgi->url({-base=>1});
+}else {
+  $vars->{siteurl} = $cgi->url({-base=>1}).dirname($vars->{basepath});
+}
+
 if ($vars->{filepath} ne ""){
   $vars->{suffix} = substr($vars->{filepath},rindex($vars->{filepath},'.')); 
   $vars->{page} = $vars->{filepath};
index 7bbc9af..532425d 100644 (file)
@@ -16,8 +16,8 @@ tinymce.init({
   image_advtab: true,
   init_instance_callback: function (editor) {
     editor.on('blur', function (e) {
-      console.log('Editor was blurred!');
-      console.log(e.target.id);
+      // console.log('Editor was blurred!');
+      // console.log(e.target.id);
       $("#" + e.target.id).html(editor.getContent());
       //console.log(editor.getContent());
       if (savefield){
@@ -29,29 +29,29 @@ tinymce.init({
 
 }
 $('select').on('hidden.bs.select', function (event) {
-  console.log(event);
-  console.log(savefield);
-  console.log("ID:" + event.currentTarget.id);
+  // console.log(event);
+  // console.log(savefield);
+  // console.log("ID:" + event.currentTarget.id);
   savefield(event.currentTarget.id);
 });
 
 
 
 $("input,textarea").on('blur',function(event){
-  console.log(event);
+  // console.log(event);
   //console.log(event.currentTarget.tagName);
   //console.log(event.currentTarget.type);
   if (event.currentTarget.type != "file"){
-    console.log('savefield');
+    //console.log('savefield');
     savefield(event.currentTarget.id);
   }
 });
 
 
 function CropImageDlg(objid){
-  console.log("Crop Object ID:"+  objid);
+  // console.log("Crop Object ID:"+  objid);
   var obj = document.getElementById("files_" + objid);
-  console.log(obj.files);
+  // console.log(obj.files);
   var file = obj.files[0];
   // console.log(file);
   var fileName = file.name;
@@ -90,7 +90,7 @@ $('#cropimg').on('shown.bs.modal', function () {
   canvasData = cropper.getCanvasData();
   var data = cropper.getCroppedCanvas().toDataURL();
   var fieldid=$('#cropdata_field_id').val();
-  console.log("FieldID:" + fieldid);
+  //console.log("FieldID:" + fieldid);
   $("#preview_" + fieldid).prop("src",data);
   $("#" + fieldid).val(data);
   savefield(fieldid);
@@ -108,7 +108,7 @@ function removephoto(objid,defaultimg){
 }
 
 function LoadCropper(objid){
-  console.log("Preview load cropper!" + objid);
+  // console.log("Preview load cropper!" + objid);
   var objf = document.getElementById('files_' + objid);
   $('#cropdata_field_id').val(objid);
   
@@ -118,3 +118,4 @@ function LoadCropper(objid){
   return false;
 }
 
+
index fda2005..e71b291 100644 (file)
@@ -8,7 +8,7 @@ left join public.lawyercategories lca on (ca.id=lca.id_category and lca.id_catal
 <header>
     <nav class="navbar navbar-expand-md navbar-light fixed-top bg-light">
     <ul class="navbar-nav mr-auto">
-      URL du profile:&nbsp;<a href="[% siteurl %]avocat/[% cat.profile_link %]" target="_blank">[% siteurl %]/avocat/[% cat.profile_link %].html</a>
+      URL du profile:&nbsp;<a href="[% siteurl %]/avocat/[% cat.profile_link %]" target="_blank">[% siteurl %]/avocat/[% cat.profile_link %].html</a>
     </ul>
     <ul class="navbar-nav ml-auto">
           <li class="nav-item">
@@ -27,13 +27,13 @@ left join public.lawyercategories lca on (ca.id=lca.id_category and lca.id_catal
     </nav>
   </header>
 <main style="margin-top: 55px;">
-<form id="frm_annuaire" name="frm_annuaire" data-ident_lawyercatalog_id="[% cat.id %]" data-ident_lawyercatalog_id_user="[% session.id %]" class="needs-validation" novalidate>
+<form id="frm_annuaire" name="frm_annuaire" data-ident_lawyercatalog_id="[% cat.id %]" data-ident_lawyercatalog_id_user="[% session.id %]">
  
 <div class="row">
   <div class="col-sm-4">
     <div class="form-group col-sm-12">
       <label for="surname">Nom</label>
-      <input type="text" class="form-control" id="surname" name="lawyercatalog_surname" required value="[% cat.surname %]" >
+      <input type="text" class="form-control" id="surname" name="lawyercatalog_surname"  required value="[% cat.surname %]" >
     </div>
     <div class="form-group col-sm-12">
       <label for="prename">Prénom</label>
index 6e17405..132796b 100644 (file)
@@ -17,7 +17,7 @@ var iduser=[% session.id %];
   }
 
   $("input.fieldsave").on('blur',function(event){
-    console.log(event);
+    //console.log(event);
     savefield(event.currentTarget.id);
   });
   
@@ -75,7 +75,7 @@ var iduser=[% session.id %];
   }
 
   function voucher_callback(data){
-    console.log(data);
+    //console.log(data);
     if (data && data.package && data.app){
       $("#checkout_package").html("Package: <strong>" + data.package + "</strong>");
       $("#checkout_package").show();
@@ -109,9 +109,13 @@ var iduser=[% session.id %];
   }
 
   function activate_app(){
-    if (!$("#frm_invoicedata").hasClass("was-validated")){
+    //console.log(frmvalidate.validate());
+    if (frmvalidate.validate() == false){
+    // if (!$("#frm_invoicedata").hasClass("was-validated")){
       return false;
     }
+    $("#checkout").hide();
+    $("#loader").show();
     var pdata = {fn:"activateapp", appinfo:{},invoice:{}};
      //if ($("#frm_invoicedata").hasClass("was-validated")){
       $("input[type='hidden']").each(function(){
@@ -124,48 +128,23 @@ var iduser=[% session.id %];
        });
        pdata['invoice']['description'] = "Souscription juridig.lu - " + appdata[pdata["appinfo"]['id_app']]["name"] + ' - ' + pdata["appinfo"]["package"];   
         
-       console.log(pdata);
+       //console.log(pdata);
        process_data(pdata,activate_callback);
-        console.log("Data Process finished!");
+        //console.log("Data Process finished!");
         return false;
-     //}
-     //return false;
-    // if ($("#frm_invoicedata").hasClass("was-validated")){
-    //   var pdata = {fn:"activateapp"};
-    
-    //   $("#frm_invoicedata input,select").each(function(){
-    //     console.log($(this).attr("id") = $(this).val()) ;
-    //     pdata[$(this).attr("id")] =  $(this).val();
-    //   });
-    
-    //   
-    // }else {
-    //   console.log("No validation!");
-    // }
-    // return false;
+
   }
 
-// function buy_viainvoice(){
-//   if (!$("#frm_invoicedata").hasClass("was-validated")){
-//     console.log("Validation NOT OK!RETURN");
-//     return false;
-//   } 
-//   var pdata = {fn:"activateapp"};
-//   $("input,select").each(function(){
-//     pdata[$(this).attr("id")] =  $(this).val();
-//   });
-//   console.log(pdata);
-//   process_data(pdata,lauchinvoice_callback);
-// }
+
 
   function activate_callback(data){
-    console.log("activated callback");
+    //console.log("activated callback");
     //console.log(JSON.stringify(data));
     parent.location.reload();
   }
 
   function paypal_callback(data){
-    console.log("Paypal callback");
+    //console.log("Paypal callback");
     //console.log(data);
     parent.location.reload();
   }
@@ -200,24 +179,7 @@ var iduser=[% session.id %];
 
 }).render('#paypal-button-container'); 
 
- (function() {
-  'use strict';
-  window.addEventListener('load', function() {
-    // Fetch all the forms we want to apply custom Bootstrap validation styles to
-    var forms = document.getElementsByClassName('needs-validation');
-    // Loop over them and prevent submission
-    var validation = Array.prototype.filter.call(forms, function(form) {
-      form.addEventListener('submit', function(event) {
-        // console.log("Check Validate");
-        if (form.checkValidity() === false) {
-          event.preventDefault();
-          event.stopPropagation();
-        }
-        form.classList.add('was-validated');
-        event.preventDefault();
-        event.stopPropagation();
-        
-      }, false);
-    });
-  }, false);
-})();
\ No newline at end of file
+var frmvalidate =new Validator(document.querySelector('#frm_invoicedata'), function (err, res) {
+  //console.log("Validator: " + res);
+  return res;
+});
index 905e28c..d079077 100644 (file)
             <div class="card col-sm-12" id="invadrdata">
             
                 <div class="card-body">
-                  <form id="frm_invoicedata"  name="frm_invoicedata" class="needs-validation" novalidate>
+                  <form id="frm_invoicedata"  name="frm_invoicedata">
                   <input type="hidden" id="id_user" name="id_user" value="[% session.id %]"/>
                   <input type="hidden" id="id_app" name="id_app" value=""/>
                   <input type="hidden" id="package" name="package" value=""/>
                   
                   <div class="form-group form-inline frmdata">
                                         <label for="email" class="col-sm-2">Email</label>
-                                        <input type="email" class="form-control col-sm-10" id="email" name="email"   required  value="[% session.username %]">
+                                        <input type="email" class="form-control col-sm-10" id="email" name="email"  data-rule="email|required" required  value="[% session.username %]">
                   </div>
                   <div class="form-group form-inline frmdata">
                                         <label for="recipient" class="col-sm-2">Nom</label>
-                                        <input type="text" class="form-control col-sm-10" id="recipient" name="recipient"   required value="[% session.prename %] [% session.surname %]">
+                                        <input type="text" class="form-control col-sm-10" id="recipient" name="recipient"   required data-rule="name|required" value="[% session.prename %] [% session.surname %]">
                   </div>
                   <div class="form-group form-inline frmdata">
                                         <label for="address" class="col-sm-2">addresse</label>
-                                        <input type="text" class="form-control col-sm-10" id="address" name="address" required value="">
+                                        <input type="text" class="form-control col-sm-10" data-rule="minlength-4|required" id="address" name="address" required value="">
                   </div>
                   <div class="form-group form-inline frmdata">
                                         <label for="userzip" class="col-sm-2">CP</label>
-                                        <input type="text" class="form-control col-sm-3" id="zip" name="zip" required value="">
+                                        <input type="text" class="form-control col-sm-3" id="zip" name="zip" data-rule="minlength-4|required" required value="">
                                         <label for="usercity" class="col-sm-1">Ville</label>
-                                        <input type="text" class="form-control col-sm-6" id="city" name="city" required value="">
+                                        <input type="text" class="form-control col-sm-6" id="city" name="city" data-rule="minlength-3|required" required value="">
                   </div>
                   <div class="form-group form-inline frmdata">
                                         <label for="countryshort" class="col-sm-2">Pays</label>
                       <!-- <button class="btn btn-primary" type="button" id="btn_invoice" onclick="buy_product();" style="margin: 2px;">Payer par facture</button> -->
                       
                       <div id="paypal-button-container"></div>
-                      <button class="btn btn-primary" type="submit" id="btn_activate" onclick="activate_app();" style="display: none;">Activer</button> 
+                      <button class="btn btn-primary" type="button" id="btn_activate" onclick="activate_app();" style="display: none;">Activer</button> 
                     </div>
                   </div>
                   </form>
             
           </div>
        
+</section>
+<section id="loader" style="display: none;">
+<div class="spinner-border" role="status">
+  <span class="sr-only">Loading...</span>
+</div>
 </section>
 <!-- <section id="payement" style="display: none;">
     <h1>Payement</h1>
index f7af729..dc34056 100644 (file)
@@ -1,3 +1,4 @@
+<script src="[% abspath %]vendors/js-form-validator/js-form-validator.js"></script>
 <script src="[% abspath %]js/fieldsave.js"></script>
 <script src="https://www.paypal.com/sdk/js?client-id=AQEcZlwruxEhpOV-lAvHNIT87tThV6ia67YiC0O8cF_IqwmLExupEPtJHFEwHEfxk0XmKZXVVF_nZRGd&currency=EUR"></script>
 <script src="[% pagename %].js"></script>
\ No newline at end of file
index 483d60a..86fb627 100644 (file)
@@ -1,8 +1,8 @@
  var apiurl = "[% baseurl %]/api/";
  function process_data(data,callback){
    
-    console.log("process data");
-    console.log(JSON.stringify(data));
+    // console.log("process data");
+    // console.log(JSON.stringify(data));
    
   strdata = ""; 
   for (var i in data){
@@ -20,8 +20,8 @@
     method: "POST",
     data: strdata,
     success: function (data){
-      console.log("returned data");
-      console.log(data);
+      // console.log("returned data");
+      // console.log(data);
       if (data && data.result){
         callback(data.result);
       } else {
diff --git a/backoffice/vendors/js-form-validator/js-form-validator.js b/backoffice/vendors/js-form-validator/js-form-validator.js
new file mode 100644 (file)
index 0000000..c4d331c
--- /dev/null
@@ -0,0 +1,873 @@
+/**
+* Simple Encapsulation Class template
+*/
+(function (root) {
+
+       "use strict";
+
+       /**
+        * Common object params
+        * @type {Object}
+        */
+       var common = {
+                       publicMethods: ['validate', 'formatString', 'destroy', 'reload', 'getFormHandle', 'getFields', 'showErrors', 'hideErrors'],
+                       className: 'Validator'
+               },
+
+               // main constructor
+               Protected = function (formHandle, submitCallback, settings) {
+
+                       formHandle.JsValidator = this;
+
+                       this.settings = {
+
+                               // Validation of a current field after the events of "change", "keyup", "blur"
+                           onAir: true,
+
+                           // Show validation errors
+                           showErrors: true,
+
+                           // Auto-hide the error messages
+                           autoHideErrors: false,
+
+                           // Timeout auto-hide error messages
+                           autoHideErrorsTimeout: 2000,
+
+                           // Language error messages
+                           locale: 'en',
+
+                           // Object for custom error messages
+                           messages: {},
+
+                           // Object for custom rules
+                           rules: {},
+
+                           // classname for error messages
+                           errorClassName: 'error',
+
+                           // remove spaces from validation field values
+                           removeSpaces: false,
+
+                           // tracking of new elements
+                           autoTracking: true,
+
+                           // events list for binding
+                           eventsList: ['keyup', 'change', 'blur']
+                       };
+
+
+
+
+
+
+
+
+
+                       
+
+                       var self = this;
+
+                       // set handle
+                       this.formHandle = formHandle || null;
+
+                       // set callback
+                       this.submitCallback = submitCallback || null;
+
+                       // get fields and rules
+                       this.fields = this.getFields(this.formHandle.querySelectorAll('[data-rule]'));
+
+
+
+
+
+                       // apply custom settings
+                       this.applySettings(settings || {});
+
+
+
+
+
+
+                       this.submitCallback = this.submitCallback.bind(this);
+                       this._eventChangeWithDelay = this._eventChangeWithDelay.bind(this);
+                       this._eventChange = this._eventChange.bind(this);
+                       this._eventSubmit = this._eventSubmit.bind(this);
+
+
+
+                       // bind events
+                       this.submitCallback && this.eventsBuilder('addEventListener');
+
+
+
+                       
+
+
+                       
+                       // autotracking for new form elements
+                       this.settings.autoTracking && ('MutationObserver' in window) && new MutationObserver(function(mutationRecords) {
+
+                           [].forEach.call(mutationRecords, function (mutation) {
+                               switch (mutation.type) {
+                                   case 'subtree':
+                                   case 'childList':
+                                       
+                                       var reloadFlag = false,
+                                               childsArray = [];
+
+                                       [].forEach.call(mutation.addedNodes, function (targetElem) {
+
+                                               childsArray = targetElem.querySelectorAll ? targetElem.querySelectorAll('*') : [];
+
+                                               if (['SELECT', 'INPUT', 'TEXTAREA', 'CHECKBOX', 'RADIOBUTTON'].indexOf(targetElem.tagName) !== -1) {
+                                                       reloadFlag = true;
+                                               };
+
+                                               !reloadFlag && [].forEach.call(childsArray, function (elem) {
+                                                       if (['SELECT', 'INPUT', 'TEXTAREA', 'CHECKBOX', 'RADIOBUTTON'].indexOf(elem.tagName) !== -1) {
+                                                               reloadFlag = true;
+                                                       }
+                                               });
+
+                                               
+                                       });
+                                       reloadFlag && self.reload();
+                                   break;
+                               }
+                           });
+
+                       }).observe(this.formHandle, {
+                           childList: true,
+                           subtree: true
+                       });
+
+                       
+
+                       return this;
+               };
+
+
+       /**
+        * Main prototype
+        * @type {Object}
+        */
+       Protected.prototype = {
+
+
+
+
+
+               messages: {
+                                           
+                       // English
+                   en: {
+                       required: {
+                           empty: 'This field is required',
+                           incorrect: 'Incorrect value'
+                       },
+                       notzero: {
+                           empty: 'Please make a selection',
+                           incorrect: 'Incorrect value'
+                       },
+                       integer: {
+                           empty: 'Enter an integer value',
+                           incorrect: 'Incorrect integer value'
+                       },
+                       float: {
+                           empty: 'Enter an float number',
+                           incorrect: 'Incorrect float'
+                       },
+                       min: {
+                           empty: 'Enter more',
+                           incorrect: 'Enter more'
+                       },
+                       max: {
+                           empty: 'Enter less',
+                           incorrect: 'Enter less'
+                       },
+                       between: {
+                           empty: 'Enter the between {0}-{1}',
+                           incorrect: 'Enter the between {0}-{1}'
+                       },
+                       name: {
+                           empty: 'Please, enter your name',
+                           incorrect: 'Incorrect name'
+                       },
+                       lastname: {
+                           empty: 'Please, enter your lastname',
+                           incorrect: 'Incorrect lastname'
+                       },
+                       phone: {
+                           empty: 'Please, enter the phone number',
+                           incorrect: 'Incorrect phone number'
+                       },
+                       email: {
+                           empty: 'Please, enter your email address',
+                           incorrect: 'Incorrect email address'
+                       },
+                       length: {
+                           empty: 'Please, Enter a minimum of {0} characters and a maximum of {1}',
+                           incorrect: 'Incorrect. Enter a minimum of {0} characters and a maximum of {1}'
+                       },
+                       minlength: {
+                           empty: 'Please, enter at least {0} characters',
+                           incorrect: 'You have entered less than {0} characters'
+                       },
+                       maxlength: {
+                           empty: 'Please, enter at maximum {0} characters',
+                           incorrect: 'You have entered more than {0} characters'
+                       },
+                       maxfilesize: {
+                           empty: 'The size of one or more selected files larger than {0} {1}',
+                           incorrect: 'The size of one or more selected files larger than {0} {1}'
+                       },
+                       fileextension: {
+                           empty: 'Select file',
+                           incorrect: 'One or more files have an invalid type'
+                       }
+                   }
+               },
+
+               // rules
+               rules: {
+                   required: function (value) {
+                       return '' !== value;
+                   },
+                   notzero: function (value) {
+                       return parseInt(value, 10) > 0;
+                   },
+                   integer: function (value) {
+                       return new RegExp(/^[0-9]+$/gi).test(value);
+                   },
+                   float: function (value) {
+                       value = value.toString().replace(/\,/, '.');
+                       return this.integer(value) || new RegExp(/^([0-9])+(\.)([0-9]+$)/gi).test(value);
+                   },
+                   min: function (value, params) {
+                       if (this.float(value)) {
+                           return parseFloat(value) >= parseFloat(params[0]);
+                       }
+                       return parseInt(value, 10) >= parseInt(params[0], 10);
+                   },
+                   max: function (value, params) {
+                       if (this.float(value)) {
+                           return parseFloat(value) <= parseFloat(params[0]);
+                       }
+                       return parseInt(value, 10) <= parseInt(params[0], 10);
+                   },
+                   between: function (value, params) {
+                       
+                       params[1] = params[1] || 999999;
+
+                       if (this.float(value)) {
+                           return parseFloat(value) >= parseFloat(params[0]) && parseFloat(value) <= parseFloat(params[1]);
+                       }
+                       if (this.integer(value)) {
+                           return parseInt(value, 10) >= parseInt(params[0], 10) && parseInt(value, 10) <= parseInt(params[1], 10);
+                       }
+                       return false;
+                   },
+                   name: function (value) {
+                       if (value.length > 0 && value.length < 2) {
+                           return false;
+                       }
+                       return new RegExp(/^[a-zA-Z\sа-яА-ЯёЁ\-]+$/g).test(value);
+                   },
+                   lastname: function (value) {
+                       return this.name(value);
+                   },
+                   phone: function (value) {
+                       if (value.replace(/[^0-9]+/gi, '').match(/[0-9]+/gi) && value.replace(/[^0-9]+/gi, '').match(/[0-9]+/gi)[0].length < 6) {
+                           return false;
+                       }
+                       return new RegExp(/^(?:(?:\(?(?:00|\+)([1-4]\d\d|[1-9]\d?)\)?)?[\-\.\ \\\/]?)?((?:\(?\d{1,}\)?[\-\.\ \\\/]?){0,})(?:[\-\.\ \\\/]?(?:#|ext\.?|extension|x)[\-\.\ \\\/]?(\d+))?$/g).test(value);
+                   },
+                   email: function (value) {
+                       return new RegExp(/^(("[\w-\s]+")|([\w\-]+(?:\.[\w\-]+)*)|("[\w-\s]+")([\w\-]+(?:\.[\w\-]+)*))(@((?:[\w\-]+\.)*\w[\w\-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$)|(@\[?((25[0-5]\.|2[0-4][0-9]\.|1[0-9]{2}\.|[0-9]{1,2}\.))((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\.){2}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\]?$)/i).test(value);
+                   },
+                   length: function (value, params) {
+                       return this.between(value.replace(/\s{2,}/g, ' ').length, params);
+                   },
+                   maxlength: function (value, params) {
+                       return this.max(value.replace(/\s{2,}/g, ' ').length, params);
+                   },
+                   minlength: function (value, params) {
+                       return this.min(value.replace(/\s{2,}/g, ' ').length, params);
+                   },
+                   maxfilesize: function (value, params) {
+                       var i,
+                           l = value.length,
+                           unitsOffset = 1;
+
+                       switch (params[1].toLowerCase()) {
+                       case 'b':
+                           unitsOffset = 1;
+                           break;
+
+                       case 'kb':
+                           unitsOffset = 1024;
+                           break;
+
+                       case 'mb':
+                           unitsOffset = 1048576;
+                           break;
+
+                       case 'gb':
+                           unitsOffset = 1073741824;
+                           break;
+
+                       case 'tb':
+                           unitsOffset = 1099511627776;
+                           break;
+                       }
+
+                       for (i = 0; i < l; i += 1) {
+                           if (parseFloat(value[i]) > (parseFloat(params[0]) * unitsOffset)) {
+                               return false;
+                           }
+                       }
+
+                       return true;
+                   },
+                   fileextension: function (value, params) {
+                       var i,
+                           a,
+                           l = params.length,
+                           b = value.length,
+                           cmpResC = 0;
+
+                       for (i = 0; i < l; i += 1) {
+                           for (a = 0; a < b; a += 1) {
+                               if (params[i] === value[a].split('.').pop()) {
+                                   cmpResC += 1;
+                               }
+                           }
+                       }
+
+                       return value.length === cmpResC ? true : false;
+                   }
+               },
+
+               orderFields: function (attrName, attrValue) {
+
+                   var self = this,
+                       retObj = {};
+
+                   !!attrName && !!attrValue && Object.keys(this.fields).forEach(function (field) {
+                       if (self.fields[field].handle[attrName] && self.fields[field].handle[attrName] === attrValue) {
+                           retObj[field] = self.fields[field];
+                       }
+                   });
+
+                   return retObj;
+               },
+               _eventSubmit: function (e) {
+
+                   e.preventDefault();
+
+                   //hide errors
+                   this.hideErrors(false, true);
+
+                   //show errors if validation failure
+                   !this.validate() && this.showErrors();
+
+                   //callback
+                   (this.submitCallback(this.errors || null, this.errors ? false : true) === true) && this.formHandle.submit();
+               },
+               _eventChange: function (e) {
+
+                       var radioBtns,
+                               self = this;
+
+                       //remove spaces
+                       if (this.settings.removeSpaces && new RegExp(/\s{2,}/g).test(e.target.value)) {
+                           e.target.value = e.target.value.replace(/\s{2,}/g, ' ');
+                       }
+
+                       //if is radio buttons
+                       if (e.target.type === 'radio') {
+
+                           //get radio groupe
+                           radioBtns = this.orderFields('name', e.target.name);
+
+                           Object.keys(radioBtns).forEach(function (btn) {
+                               self.hideErrors(radioBtns[btn].handle);
+                           });
+
+                       } else {
+                           //hide errors for this
+                           this.hideErrors(e.target);
+                       }
+
+
+
+
+                       //validate and show errors for this
+                       if (!this.validate(e.target)) {
+
+                           this.showErrors(e.target);
+                           !this.settings.showErrors && this.submitCallback(this.errors, false);
+                           
+                       }
+               },
+               _eventChangeWithDelay: function (e) {
+                       var self = this;
+
+                   if (this.intervalID) {
+                       clearTimeout(this.intervalID);
+                   }
+
+                   this.intervalID = setTimeout(function () {
+                       self._eventChange.apply(self, [e]);
+                   }, 400);
+               },
+               
+
+               applySettings: function (settings) {
+
+                       var self = this;
+
+                       // apply rules
+                       settings.rules && Object.keys(settings.rules).forEach(function(ruleName) {
+                               self.rules[ruleName] = settings.rules[ruleName];
+                       });
+
+                       // apply messages
+                       settings.messages && Object.keys(settings.messages).forEach(function(locale) {
+                           Object.keys(settings.messages[locale]).forEach(function (ruleName) {
+                               Object.keys(settings.messages[locale][ruleName]).forEach(function (param) {
+                                       self.settings.messages[locale] = self.settings.messages[locale] || {};
+                                       self.settings.messages[locale][ruleName] = self.settings.messages[locale][ruleName] || {};
+                                       self.settings.messages[locale][ruleName][param] = settings.messages[locale][ruleName][param];
+                               });
+                           });
+                       });
+
+                       // apply other settings
+                       Object.keys(settings).forEach(function (param) {
+                               self.settings[param] = settings[param];
+                       });
+
+                       return this;
+               },
+
+
+               getFields: function (fields) {
+
+                       var retData = {},
+                               rules = [],
+                               params = [];
+
+                       fields = fields || this.formHandle.querySelectorAll('[data-rule]');
+
+                       // each fields with data-rule attribute
+                       Object.keys(fields).forEach(function (fieldIndex) {
+
+                               rules = fields[fieldIndex].getAttribute('data-rule').split('|');
+
+                               Object.keys(rules).forEach(function (ruleIndex) {
+
+                                       // parse rule
+                                       if (rules[ruleIndex].match(/-/gi)) {
+
+                                           params = rules[ruleIndex].split('-');
+                                           rules[ruleIndex] = params[0];
+                                           params = params.splice(1);
+
+                                           rules[ruleIndex] = [rules[ruleIndex], params];
+                                       } else {
+                                           rules[ruleIndex] = [rules[ruleIndex], []];
+                                       }
+                               });
+
+                               retData[fieldIndex] = {
+                                   name: fields[fieldIndex].getAttribute('name'),
+                                   rules: rules,
+                                   defaultValue: fields[fieldIndex].getAttribute('data-default'),
+                                   handle: fields[fieldIndex],
+                                   intervalID: null
+                               };
+                       });
+
+                       return retData;
+               },
+
+               validate: function (validationField) {
+
+                       var self = this,
+                               fields = validationField ? this.getFields([validationField]) : this.fields,
+                               result,
+                               ruleName,
+                               params,
+                               defaultValue,
+                               value,
+                               message,
+                               messageType = null;
+
+                       this.errors = this.errors ? null : this.errors;
+
+                       Object.keys(fields).forEach(function (n) {
+                               
+                               result = true;
+
+                               // loop rules of this field
+                               fields[n].rules && Object.keys(fields[n].rules).forEach(function (ruleIndex) {
+                                       
+                                       // set rule data
+                                       ruleName = fields[n].rules[ruleIndex][0];
+                                       params = fields[n].rules[ruleIndex][1];
+                                       defaultValue = fields[n].defaultValue;
+                                       value = fields[n].handle.value;
+
+
+                                       switch (fields[n].handle.type) {
+                                               
+                                               case 'checkbox':
+                                                       !fields[n].handle.checked && (value = '');
+                                               break;
+
+                                               case 'radio':
+                                                       // get radio groupe
+                                                       var radioBtns = self.orderFields('name', fields[n].handle.name),
+                                                               checked = false;
+
+                                                       Object.keys(radioBtns).forEach(function (i) {
+                                                               radioBtns[i].handle.checked && (checked = true);
+                                                       });
+
+                                                       if (!checked) {
+                                                           
+                                                           // add an error to one element
+                                                           Object.keys(radioBtns).forEach(function (i) {
+                                                               try {
+                                                                   message = self.settings.messages[self.settings.locale][ruleName].empty;
+                                                               } catch (e) {
+                                                                   message = self.messages[self.settings.locale][ruleName].empty;
+                                                               }
+                                                           });
+
+                                                           // set value as for empty rules
+                                                           value = '';
+                                                       }
+                                               break;
+
+                                               case 'file':
+
+                                                       // if the files were selected
+                                                       if (fields[n].handle.files && fields[n].handle.files.length) {
+
+                                                               value = [];
+
+                                                               Object.keys(fields[n].handle.files).forEach(function (fileIndex) {
+
+                                                                       switch (ruleName) {
+                                                                               case 'maxfilesize':
+                                                                                       value.push(fields[n].handle.files[fileIndex].size);
+                                                                               break;
+
+                                                                               case 'fileextension':
+                                                                                       value.push(fields[n].handle.files[fileIndex].name);
+                                                                               break;
+                                                                       }
+                                                               });
+
+                                                       }
+
+                                               break;
+                                       }
+
+
+                                       if (result && !(value === '' && !fields[n].rules.join('|').match(/\|{0,1}required\|{0,1}/))) {
+
+                                               // if exist default value and value is eq default
+                                               if (result && defaultValue && value !== defaultValue) {
+
+                                                   result = false;
+                                                   messageType = 'incorrect';
+
+                                               // if default value not exist
+                                               } else if (result && self.rules[ruleName] && !self.rules[ruleName](value, params)) {
+
+                                                   // set message to empty data
+                                                   if ('' === value) {
+                                                       result = false;
+                                                       messageType = 'empty';
+
+                                                   // set message to incorrect data
+                                                   } else {
+                                                       result = false;
+                                                       messageType = 'incorrect';
+                                                   }
+                                               }
+
+                                               if (result) {
+                                                   self.hideErrors(fields[n].handle, true);
+                                               
+                                               } else {
+
+                                                   // define errors stack if not exist
+                                                   self.errors = self.errors || {};
+
+                                                   // append error messages
+                                                   if (ruleName === 'required' && fields[n].rules[1] && fields[n].rules[1][0]) {
+                                                       ruleName = fields[n].rules[1][0];
+                                                       messageType = 'empty';
+                                                   }
+                                                   
+                                                   try {
+                                                       try {
+                                                           message = self.settings.messages[self.settings.locale][ruleName][messageType];
+                                                       } catch (e) {
+                                                           message = self.messages[self.settings.locale][ruleName][messageType];
+                                                       }
+                                                   } catch (e) {
+                                                       ruleName = 'required';
+                                                       message = self.messages[self.settings.locale][ruleName][messageType];
+                                                   }
+
+                                                   // push value into params if params is empty
+                                                   !params.length && params.push(value);
+
+                                                   // add errors
+                                                   self.errors[n] = {
+                                                       name: fields[n].name,
+                                                       errorText: self.formatString(message, params)
+                                                   };
+
+                                                   // call callback if exist
+                                                   if (!self.submitCallback) {
+                                                       self.errors[n].handle = fields[n].handle;
+                                                   }
+                                               }
+                                       }
+                               });
+                       });
+
+
+                       // run callback if callback is exists and not errors or return error data object
+                       if (this.submitCallback) {
+                           return (this.errors) ? false : true;
+                       }
+
+                       return this.errors || true;
+
+               },
+
+
+               hideErrors: function (validationField, removeClass) {
+
+                   var self = this,
+                       errorDiv;
+
+
+                       Object.keys(this.fields).forEach(function (n) {
+                       if ((validationField && validationField === self.fields[n].handle) || !validationField) {
+
+                               errorDiv = self.fields[n].handle.nextElementSibling;
+
+                               // remove class error
+                                       removeClass && self.fields[n].handle.classList.remove(self.settings.errorClassName);
+
+                                       // remove error element
+                               errorDiv && (errorDiv.getAttribute('data-type') === 'validator-error') && errorDiv.parentNode.removeChild(errorDiv);
+                       }
+                       });
+
+               },
+
+               showErrors: function (validationField) {
+
+                       var self = this,
+                               errorDiv,
+                               insertNodeError = function (refNode, errorObj) {
+
+                                       // set error class
+                                       refNode.classList.add(self.settings.errorClassName);
+
+                                       // check to error div element exist
+                                       if (refNode.nextElementSibling && refNode.nextElementSibling.getAttribute('data-type') === 'validator-error') {
+                                               return;
+                                       }
+
+                                       // insert error element
+                                       if (self.settings.showErrors) {
+                                               errorDiv = document.createElement('div');
+                                               errorDiv.setAttribute('class', self.settings.errorClassName);
+                                               errorDiv.setAttribute('data-type', 'validator-error');
+                                               errorDiv.innerHTML = errorObj.errorText;
+                                               refNode.parentNode.insertBefore(errorDiv, refNode.nextSibling);
+                                       }
+                               };
+
+
+
+
+                       Object.keys(this.errors).forEach(function (r) {
+                               
+                               // show error to specified field
+                               if (validationField) {
+
+                                       Object.keys(self.fields).forEach(function (n) {
+                                               (self.fields[n].handle.getAttribute('name') === validationField.getAttribute('name')) && insertNodeError(self.fields[n].handle, self.errors[r]);
+                                       });
+
+                               // show error to all fields
+                               } else {
+                                   if (r === '0' || (r > 0 && self.fields[r].name !== self.fields[r - 1].name)) {
+                                       insertNodeError(self.fields[r].handle, self.errors[r]);
+                                   }
+                               }
+                       });
+
+
+
+
+
+                       // auto hide errors
+                       if (this.settings.autoHideErrors) {
+                               
+                               // for all fields
+                               if (!validationField) {
+
+                                   if (this.intervalID) {
+                                       clearTimeout(this.intervalID);
+                                   }
+
+                                   this.intervalID = setTimeout(function () {
+                                       self.intervalID = null;
+                                       self.hideErrors(false);
+                                   }, this.settings.autoHideErrorsTimeout);
+
+                               // for current field
+                               } else {
+
+                                   if (validationField.intervalID) {
+                                       clearTimeout(validationField.intervalID);
+                                   }
+
+                                   if (!this.intervalID) {
+                                       validationField.intervalID = setTimeout(function () {
+                                           validationField.intervalID = null;
+                                           self.hideErrors(validationField);
+                                       }, this.settings.autoHideErrorsTimeout);
+                                   }
+                               }
+                       }
+               },
+
+
+               /*
+               * Get Form handle
+               * @return {element} - Form handle
+               */
+               getFormHandle: function () {
+                   return this.formHandle;
+               },
+
+               /*
+               * Formatting string. Replace string
+               * @param {string} string - Source string. Example: "{0} age {1} years."
+               * @param {array} params - An array of values​​, which will be replaced with markers. Example: ['Bob', 36]
+               * @return {string} - Formatted string with replacing markers. Example "Bob age 36 years"
+               */
+               formatString: function (string, params) {
+                   return string.replace(/\{(\d+)\}/gi, function (match, number) {
+                       return (match && params[number]) ? params[number] : '';
+                   });
+               },
+
+               /*
+               * Destroy validator
+               */
+               destroy: function () {
+                  
+                   //hide errors
+                   this.hideErrors(false, true);
+
+                   // remove events
+                   this.eventsBuilder('removeEventListener');
+
+               },
+
+               /*
+               * Reload validator.
+               * Example 1: reload(function (err, res) {...}, {autoHideErrors: false})
+               * Example 2: reload({autoHideErrors: false})
+               * @param {function} [submitCallback] - Submit callback function
+               * @param {object} [settings] - Settings object
+               */
+               reload: function (submitCallback, settings) {
+
+                       this.destroy();
+
+                   //set variables
+                   switch (arguments.length) {
+
+                   case 2:
+                       this.submitCallback = submitCallback;
+                       this.settings = settings;
+                       break;
+
+                   case 1:
+                       this.settings = submitCallback;
+                       break;
+                   }
+
+                   this.fields = this.getFields(this.formHandle.querySelectorAll('[data-rule]'));
+                   this.submitCallback && this.eventsBuilder('addEventListener');
+                   this.applySettings(settings || {});
+
+               },
+               eventsBuilder: function (actionName) {
+
+                       var self = this;
+
+
+                       this.formHandle[actionName]('submit', this._eventSubmit);
+
+                       // air mode
+                       this.settings.onAir && Object.keys(this.fields).forEach(function (field) {
+                               
+                               [].forEach.call(self.settings.eventsList, function (event) {
+
+                                       if (event === 'keyup') {
+                                           self.fields[field].handle[actionName](event, self._eventChangeWithDelay);
+                                       } else {
+                                           self.fields[field].handle[actionName](event, self._eventChange);
+                                       }
+                               });
+                       });
+                       
+                       
+               }
+       };
+
+       /**
+        * Encapsulation
+        * @return {Object} - this handle
+        */
+       root[common.className] = function () {
+
+               function construct(constructor, args) {
+
+                       function Class() {
+                               return constructor.apply(this, args);
+                       }
+                       Class.prototype = constructor.prototype;
+                       return new Class();
+               }
+
+               var original = construct(Protected, arguments),
+                       Publicly = function () {};
+
+               Publicly.prototype = {};
+               [].forEach.call(common.publicMethods, function (member) {
+                       Publicly.prototype[member] = function () {
+                               return original[member].apply(original, arguments);
+                       };
+               });
+
+               return new Publicly(arguments);
+       };
+
+}(this));
\ No newline at end of file
diff --git a/backoffice/vendors/js-form-validator/js-form-validator.min.js b/backoffice/vendors/js-form-validator/js-form-validator.min.js
new file mode 100644 (file)
index 0000000..e20ba5a
--- /dev/null
@@ -0,0 +1 @@
+(function(q){var r="validate formatString destroy reload getFormHandle getFields showErrors hideErrors".split(" "),m=function(a,b,c){a.JsValidator=this;this.settings={onAir:!0,showErrors:!0,autoHideErrors:!1,autoHideErrorsTimeout:2E3,locale:"en",messages:{},rules:{},errorClassName:"error",removeSpaces:!1,autoTracking:!0,eventsList:["keyup","change","blur"]};var d=this;this.formHandle=a||null;this.submitCallback=b||null;this.fields=this.getFields(this.formHandle.querySelectorAll("[data-rule]"));this.applySettings(c||{});this.submitCallback=this.submitCallback.bind(this);this._eventChangeWithDelay=this._eventChangeWithDelay.bind(this);this._eventChange=this._eventChange.bind(this);this._eventSubmit=this._eventSubmit.bind(this);this.submitCallback&&this.eventsBuilder("addEventListener");this.settings.autoTracking&&"MutationObserver"in window&&(new MutationObserver(function(a){[].forEach.call(a,function(a){switch(a.type){case "subtree":case "childList":var b=!1,c=[];[].forEach.call(a.addedNodes,function(a){c=a.querySelectorAll?a.querySelectorAll("*"):[];-1!==["SELECT","INPUT","TEXTAREA","CHECKBOX","RADIOBUTTON"].indexOf(a.tagName)&&(b=!0);!b&&[].forEach.call(c,function(a){-1!==["SELECT","INPUT","TEXTAREA","CHECKBOX","RADIOBUTTON"].indexOf(a.tagName)&&(b=!0)})});b&&d.reload()}})})).observe(this.formHandle,{childList:!0,subtree:!0});return this};m.prototype={messages:{en:{required:{empty:"This field is required",incorrect:"Incorrect value"},notzero:{empty:"Please make a selection",incorrect:"Incorrect value"},integer:{empty:"Enter an integer value",incorrect:"Incorrect integer value"},"float":{empty:"Enter an float number",incorrect:"Incorrect float"},min:{empty:"Enter more",incorrect:"Enter more"},max:{empty:"Enter less",incorrect:"Enter less"},between:{empty:"Enter the between {0}-{1}",incorrect:"Enter the between {0}-{1}"},name:{empty:"Please, enter your name",incorrect:"Incorrect name"},lastname:{empty:"Please, enter your lastname",incorrect:"Incorrect lastname"},phone:{empty:"Please, enter the phone number",incorrect:"Incorrect phone number"},email:{empty:"Please, enter your email address",incorrect:"Incorrect email address"},length:{empty:"Please, Enter a minimum of {0} characters and a maximum of {1}",incorrect:"Incorrect. Enter a minimum of {0} characters and a maximum of {1}"},minlength:{empty:"Please, enter at least {0} characters",incorrect:"You have entered less than {0} characters"},maxlength:{empty:"Please, enter at maximum {0} characters",incorrect:"You have entered more than {0} characters"},maxfilesize:{empty:"The size of one or more selected files larger than {0} {1}",incorrect:"The size of one or more selected files larger than {0} {1}"},fileextension:{empty:"Select file",incorrect:"One or more files have an invalid type"}}},rules:{required:function(a){return""!==a},notzero:function(a){return 0<parseInt(a,10)},integer:function(a){return(new RegExp(/^[0-9]+$/gi)).test(a)},"float":function(a){a=a.toString().replace(/,/,".");return this.integer(a)||(new RegExp(/^([0-9])+(\.)([0-9]+$)/gi)).test(a)},min:function(a,b){return this["float"](a)?parseFloat(a)>=parseFloat(b[0]):parseInt(a,10)>=parseInt(b[0],10)},max:function(a,b){return this["float"](a)?parseFloat(a)<=parseFloat(b[0]):parseInt(a,10)<=parseInt(b[0],10)},between:function(a,b){b[1]=b[1]||999999;return this["float"](a)?parseFloat(a)>=parseFloat(b[0])&&parseFloat(a)<=parseFloat(b[1]):this.integer(a)?parseInt(a,10)>=parseInt(b[0],10)&&parseInt(a,10)<=parseInt(b[1],10):!1},name:function(a){return 0<a.length&&2>a.length?!1:(new RegExp(/^[a-zA-Z\s\u0430-\u044f\u0410-\u042f\u0451\u0401\-]+$/g)).test(a)},lastname:function(a){return this.name(a)},phone:function(a){return a.replace(/[^0-9]+/gi,"").match(/[0-9]+/gi)&&6>a.replace(/[^0-9]+/gi,"").match(/[0-9]+/gi)[0].length?!1:(new RegExp(/^(?:(?:\(?(?:00|\+)([1-4]\d\d|[1-9]\d?)\)?)?[\-\. \\\/]?)?((?:\(?\d{1,}\)?[\-\. \\\/]?){0,})(?:[\-\. \\\/]?(?:#|ext\.?|extension|x)[\-\. \\\/]?(\d+))?$/g)).test(a)},email:function(a){return(new RegExp(/^(("[\w-\s]+")|([\w\-]+(?:\.[\w\-]+)*)|("[\w-\s]+")([\w\-]+(?:\.[\w\-]+)*))(@((?:[\w\-]+\.)*\w[\w\-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$)|(@\[?((25[0-5]\.|2[0-4][0-9]\.|1[0-9]{2}\.|[0-9]{1,2}\.))((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\.){2}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\]?$)/i)).test(a)},length:function(a,b){return this.between(a.replace(/\s{2,}/g," ").length,b)},maxlength:function(a,b){return this.max(a.replace(/\s{2,}/g," ").length,b)},minlength:function(a,b){return this.min(a.replace(/\s{2,}/g," ").length,b)},maxfilesize:function(a,b){var c,d=a.length,e=1;switch(b[1].toLowerCase()){case "b":e=1;break;case "kb":e=1024;break;case "mb":e=1048576;break;case "gb":e=1073741824;break;case "tb":e=1099511627776}for(c=0;c<d;c+=1)if(parseFloat(a[c])>parseFloat(b[0])*e)return!1;return!0},fileextension:function(a,b){var c,d,e=b.length,g=a.length,h=0;for(c=0;c<e;c+=1)for(d=0;d<g;d+=1)b[c]===a[d].split(".").pop()&&(h+=1);return a.length===h?!0:!1}},orderFields:function(a,b){var c=this,d={};a&&b&&Object.keys(this.fields).forEach(function(e){c.fields[e].handle[a]&&c.fields[e].handle[a]===b&&(d[e]=c.fields[e])});return d},_eventSubmit:function(a){a.preventDefault();this.hideErrors(!1,!0);!this.validate()&&this.showErrors();!0===this.submitCallback(this.errors||null,this.errors?!1:!0)&&this.formHandle.submit()},_eventChange:function(a){var b=this;this.settings.removeSpaces&&(new RegExp(/\s{2,}/g)).test(a.target.value)&&(a.target.value=a.target.value.replace(/\s{2,}/g," "));if("radio"===a.target.type){var c=this.orderFields("name",a.target.name);Object.keys(c).forEach(function(a){b.hideErrors(c[a].handle)})}else this.hideErrors(a.target);this.validate(a.target)||(this.showErrors(a.target),!this.settings.showErrors&&this.submitCallback(this.errors,!1))},_eventChangeWithDelay:function(a){var b=this;this.intervalID&&clearTimeout(this.intervalID);this.intervalID=setTimeout(function(){b._eventChange.apply(b,[a])},400)},applySettings:function(a){var b=this;a.rules&&Object.keys(a.rules).forEach(function(c){b.rules[c]=a.rules[c]});a.messages&&Object.keys(a.messages).forEach(function(c){Object.keys(a.messages[c]).forEach(function(d){Object.keys(a.messages[c][d]).forEach(function(e){b.settings.messages[c]=b.settings.messages[c]||{};b.settings.messages[c][d]=b.settings.messages[c][d]||{};b.settings.messages[c][d][e]=a.messages[c][d][e]})})});Object.keys(a).forEach(function(c){b.settings[c]=a[c]});return this},getFields:function(a){var b={},c=[],d=[];a=a||this.formHandle.querySelectorAll("[data-rule]");Object.keys(a).forEach(function(e){c=a[e].getAttribute("data-rule").split("|");Object.keys(c).forEach(function(a){c[a].match(/-/gi)?(d=c[a].split("-"),c[a]=d[0],d=d.splice(1),c[a]=[c[a],d]):c[a]=[c[a],[]]});b[e]={name:a[e].getAttribute("name"),rules:c,defaultValue:a[e].getAttribute("data-default"),handle:a[e],intervalID:null}});return b},validate:function(a){var b=this,c=a?this.getFields([a]):this.fields,d,e,g,h,f,k,l=null;this.errors=this.errors?null:this.errors;Object.keys(c).forEach(function(a){d=!0;c[a].rules&&Object.keys(c[a].rules).forEach(function(m){e=c[a].rules[m][0];g=c[a].rules[m][1];h=c[a].defaultValue;f=c[a].handle.value;switch(c[a].handle.type){case "checkbox":!c[a].handle.checked&&(f="");break;case "radio":var n=b.orderFields("name",c[a].handle.name),p=!1;Object.keys(n).forEach(function(a){n[a].handle.checked&&(p=!0)});p||(Object.keys(n).forEach(function(a){try{k=b.settings.messages[b.settings.locale][e].empty}catch(u){k=b.messages[b.settings.locale][e].empty}}),f="");break;case "file":c[a].handle.files&&c[a].handle.files.length&&(f=[],Object.keys(c[a].handle.files).forEach(function(b){switch(e){case "maxfilesize":f.push(c[a].handle.files[b].size);break;case "fileextension":f.push(c[a].handle.files[b].name)}}))}if(d&&(""!==f||c[a].rules.join("|").match(/\|{0,1}required\|{0,1}/)))if(d&&h&&f!==h?(d=!1,l="incorrect"):d&&b.rules[e]&&!b.rules[e](f,g)&&(""===f?(d=!1,l="empty"):(d=!1,l="incorrect")),d)b.hideErrors(c[a].handle,!0);else{b.errors=b.errors||{};try{try{k=b.settings.messages[b.settings.locale][e][l]}catch(t){k=b.messages[b.settings.locale][e][l]}}catch(t){e="required",k=b.messages[b.settings.locale][e][l]}!g.length&&g.push(f);b.errors[a]={name:c[a].name,errorText:b.formatString(k,g)};b.submitCallback||(b.errors[a].handle=c[a].handle)}})});return this.submitCallback?this.errors?!1:!0:this.errors||!0},hideErrors:function(a,b){var c=this,d;Object.keys(this.fields).forEach(function(e){if(a&&a===c.fields[e].handle||!a)d=c.fields[e].handle.nextElementSibling,b&&c.fields[e].handle.classList.remove(c.settings.errorClassName),d&&"validator-error"===d.getAttribute("data-type")&&d.parentNode.removeChild(d)})},showErrors:function(a){var b=this,c,d=function(a,d){a.classList.add(b.settings.errorClassName);a.nextElementSibling&&"validator-error"===a.nextElementSibling.getAttribute("data-type")||!b.settings.showErrors||(c=document.createElement("div"),c.setAttribute("class",b.settings.errorClassName),c.setAttribute("data-type","validator-error"),c.innerHTML=d.errorText,a.parentNode.insertBefore(c,a.nextSibling))};Object.keys(this.errors).forEach(function(c){a?Object.keys(b.fields).forEach(function(e){b.fields[e].handle.getAttribute("name")===a.getAttribute("name")&&d(b.fields[e].handle,b.errors[c])}):("0"===c||0<c&&b.fields[c].name!==b.fields[c-1].name)&&d(b.fields[c].handle,b.errors[c])});this.settings.autoHideErrors&&(a?(a.intervalID&&clearTimeout(a.intervalID),this.intervalID||(a.intervalID=setTimeout(function(){a.intervalID=null;b.hideErrors(a)},this.settings.autoHideErrorsTimeout))):(this.intervalID&&clearTimeout(this.intervalID),this.intervalID=setTimeout(function(){b.intervalID=null;b.hideErrors(!1)},this.settings.autoHideErrorsTimeout)))},getFormHandle:function(){return this.formHandle},formatString:function(a,b){return a.replace(/\{(\d+)\}/gi,function(a,d){return a&&b[d]?b[d]:""})},destroy:function(){this.hideErrors(!1,!0);this.eventsBuilder("removeEventListener")},reload:function(a,b){this.destroy();switch(arguments.length){case 2:this.submitCallback=a;this.settings=b;break;case 1:this.settings=a}this.fields=this.getFields(this.formHandle.querySelectorAll("[data-rule]"));this.submitCallback&&this.eventsBuilder("addEventListener");this.applySettings(b||{})},eventsBuilder:function(a){var b=this;this.formHandle[a]("submit",this._eventSubmit);this.settings.onAir&&Object.keys(this.fields).forEach(function(c){[].forEach.call(b.settings.eventsList,function(d){if("keyup"===d)b.fields[c].handle[a](d,b._eventChangeWithDelay);else b.fields[c].handle[a](d,b._eventChange)})})}};q.Validator=function(){var a=function(a,b){function c(){return a.apply(this,b)}c.prototype=a.prototype;return new c}(m,arguments),b=function(){};b.prototype={};[].forEach.call(r,function(c){b.prototype[c]=function(){return a[c].apply(a,arguments)}});return new b(arguments)}})(this);
\ No newline at end of file