text: (function() {
  
  (function() {
  
  //  Faster operation analogues:
  //  Math.floor(f)  => (a)
  //  Math.round(f)  =>  (f + 0.5) | 0
  //
  function isString(o)  { return typeof o !== "undefined" && o !== null && (typeof o === "string" || o.constructor === String); }
  function isNumber(o)  { return typeof o !== "undefined" && o !== null && (typeof o === "number" || o.constructor === Number); }
  function isBoolean(o) { return typeof o !== "undefined" && o !== null && (typeof o === "boolean" || o.constructor === Boolean); }
  
  if (!String.prototype.trim) { String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g,'');  }; }
  
  if (!Array.prototype.indexOf) {
      Array.prototype.indexOf = function(searchElement) {
          if (this == null) throw new TypeError();
          var t = Object(this), len = t.length >>> 0;
          if (len === 0) return -1;
  
          var n = 0;
          if (arguments.length > 0) {
              n = Number(arguments[1]);
              if (n != n) n = 0;
              else if (n !== 0 && n != Infinity && n != -Infinity) {
                  n = (n > 0 || -1) * ~~Math.abs(n);
              }
          }
          if (n >= len) return -1;
          var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
          for (; k < len; k++) if (k in t && t[k] === searchElement) return k;
          return -1;
      };
  }
  
  if (!Array.isArray) Array.isArray = function(a) { return Object.prototype.toString.call(a) == '[object Array]'; };
  
  var $$$ = 0, namespaces = {}, namespace = function(nsname, dontCreate) {
      if (isString(nsname) === false) {
          throw new Error("Wrong nsname argument");
      }
  
      if (namespaces.hasOwnProperty(nsname)) {
          return namespaces[nsname];
      }
  
      if (dontCreate === true) {
          throw new Error("Namespace '" + nsname + "' doesn't exist");
      }
  
      function Package() {
          this.$url = null;
          if (zebra.isInBrowser) {
              var s  = document.getElementsByTagName('script'),
                  ss = s[s.length - 1].getAttribute('src'),
                  i  = ss == null ? -1 : ss.lastIndexOf("/");
  
              this.$url = (i > 0) ? new zebra.URL(ss.substring(0, i + 1))
                                  : new zebra.URL(document.location.toString()).getParentURL() ;
          }
      }
  
      if (isString(nsname) === false) {
          throw new Error('invalid namespace id');
      }
  
      if (namespaces.hasOwnProperty(nsname)) {
          throw new Error("Namespace '" + nsname + "' already exists");
      }
  
      var f = function(name) {
          if (arguments.length === 0) return f.$env;
  
          if (typeof name === 'function') {
              for(var k in f) if (f[k] instanceof Package) name(k, f[k]);
              return null;
          }
  
          var b = Array.isArray(name);
          if (isString(name) === false && b === false) {
              for(var k in name) if (name.hasOwnProperty(k)) f.$env[k] = name[k];
              return;
          }
  
          if (b) {
             for(var i = 0; i < name.length; i++) f(name[i]);
             return null;
          }
  
          if (f[name] instanceof Package) return f[name];
  
          var names = name.split('.'), target = f;
          for(var i = 0, k = names[0]; i < names.length; i++, k = [k, '.', names[i]].join('')) {
              var n = names[i], p = target[n];
              if (typeof p === "undefined") {
                  p = new Package();
                  target[n] = p;
                  f[k] = p;
              }
              else {
                  if ((p instanceof Package) === false) {
                      throw new Error("Package '" + name +  "' conflicts with variable '" + n + "'");
                  }
              }
  
              target = p;
          }
          return target;
      };
  
      f.Import = function() {
          var ns = ["=", nsname, "."].join(''), code = [], packages = arguments.length === 0 ? null : Array.prototype.slice.call(arguments, 0);
          f(function(n, p) {
              if (packages == null || packages.indexOf(n) >= 0) {
                  for (var k in p) {
                      if (k[0] != '$' && k[0] != '_' && (p[k] instanceof Package) === false && p.hasOwnProperty(k)) {
                          code.push([k, ns, n, ".", k].join(''));
                      }
                  }
                  if (packages != null) packages.splice(packages.indexOf(n), 1);
              }
          });
  
          if (packages != null && packages.length !== 0) {
              throw new Error("Unknown package(s): " + packages.join(","));
          }
  
          return code.length > 0 ? [ "var ", code.join(","), ";"].join('') : null;
      };
  
      f.$env = {};
      namespaces[nsname] = f;
      return f;
  };
  
  
  zebra = namespace('zebra');
  var pkg = zebra,
      FN = pkg.$FN = (typeof namespace.name === "undefined") ? (function(f) { var mt = f.toString().match(/^function ([^(]+)/); return (mt == null) ? '' : mt[1]; })
                                                             : (function(f) { return f.name; });
  
  pkg.namespaces = namespaces;
  pkg.namespace  = namespace;
  pkg.$global    = this;
  pkg.isString   = isString;
  pkg.isNumber   = isNumber;
  pkg.isBoolean  = isBoolean;
  pkg.version = "1.2.0";
  pkg.$caller = null;
  
  
  function mnf(name, params) {
      var cln = this.getClazz && this.getClazz().$name ? this.getClazz().$name + "." : "";
      throw new ReferenceError("Method '" + cln + (name === '' ? "constructor" : name) + "(" + params + ")" + "' not found");
  }
  
  function $toString() { return this._hash_; }
  function $equals(o)  { return this == o;   }
  
  // return function that is meta class
  //  pt - parent template function
  //  tf - template function
  function make_template(pt, tf, p) {
      tf._hash_ = ["$zebra_", $$$++].join('');
      tf.toString = $toString;
      if (pt != null) tf.prototype.getClazz = function() { return tf; };
      tf.getClazz = function() { return pt; };
      tf.prototype.toString = $toString;
      tf.prototype.equals   = $equals;
      tf.prototype.constructor = tf;
  
      if (p && p.length > 0) {
          tf.parents = {};
          for(var i=0; i < p.length; i++) {
              var l = p[i];
              if (typeof l === 'undefined') throw new ReferenceError("Unknown parent["+i+"]");
              tf.parents[l] = true;
              if (l.parents) {
                  var pp = l.parents;
                  for(var k in pp) {
                      if (pp.hasOwnProperty(k)) tf.parents[k] = true;
                  }
              }
          }
      }
      return tf;
  }
  
  pkg.getPropertySetter = function(obj, name) {
      var pi = obj.constructor.$propertyInfo;
      if (pi != null) {
          if (typeof pi[name] === "undefined") {
              var m = obj[ ["set", name[0].toUpperCase(), name.substring(1)].join('') ];
              pi[name] = (typeof m  === "function") ? m : null;
          }
          return pi[name];
      }
  
      var m = obj[["set", name[0].toUpperCase(), name.substring(1)].join('')];
      return (typeof m  === "function") ? m : null;
  };
  
  pkg.Interface = make_template(null, function() {
      var $Interface = make_template(pkg.Interface, function() {
          if (arguments.length > 0) return new (pkg.Class($Interface, arguments[0]))();
      }, arguments);
      return $Interface;
  });
  
  pkg.$Extended = pkg.Interface();
  
  function ProxyMethod(name, f) {
      if (isString(name) === false) {
          throw new TypeError('Method name has not been defined');
      }
  
      var a = null;
      if (arguments.length == 1) {
          a = function() {
              var nm = a.methods[arguments.length];
              if (nm) {
                  var cm = pkg.$caller;
                  pkg.$caller = nm;
                  try { return nm.apply(this, arguments); }
                  catch(e) { throw e; }
                  finally { pkg.$caller = cm; }
              }
              mnf.call(this, a.methodName, arguments.length);
          };
          a.methods = {};
      }
      else {
          a = function() {
              var cm = pkg.$caller;
              pkg.$caller = f;
              try { return f.apply(this, arguments); }
              catch(e) { throw e; }
              finally { pkg.$caller = cm; }
          };
          a.f = f;
      }
  
      a.$clone$ = function() {
          if (a.methodName === '') return null;
          if (a.f) return ProxyMethod(a.methodName, a.f);
          var m = ProxyMethod(a.methodName);
          for(var k in a.methods) m.methods[k] = a.methods[k];
          return m;
      };
  
      a.methodName = name;
      return a;
  }
  
  pkg.Class = make_template(null, function() {
      if (arguments.length === 0) throw new Error("No class definition was found");
  
      var df = arguments[arguments.length - 1], 
          $parent = null,
          args = Array.prototype.slice.call(arguments, 0, arguments.length-1);
  
      if (args.length > 0 && (args[0] == null || args[0].getClazz() == pkg.Class)) {
          $parent = args[0];
      }
  
      var $template = make_template(pkg.Class, function() {
          this._hash_ = ["$zObj_", $$$++].join('');
  
          if (arguments.length > 0) {
              var a = arguments[arguments.length - 1];
  
              // inner is customized class instance if last arguments is array of functions
              if (Array.isArray(a) === true && typeof a[0] === 'function') {
                  a = a[0];
                  var args = [ $template ], k = arguments.length - 2;
                  for(; k >= 0 && pkg.instanceOf(arguments[k], pkg.Interface); k--) args.push(arguments[k]);
                  args.push(arguments[arguments.length - 1]);
                  var cl = pkg.Class.apply(null, args), f = function() {};
                  cl.$name = $template.$name;
                  f.prototype = cl.prototype;
                  var o = new f();
                  cl.apply(o, Array.prototype.slice.call(arguments, 0, k + 1));
                  o.constructor = cl;
                  return o;
              }
          }
  
          this[''] && this[''].apply(this, arguments);
      }, args);
  
      $template.$parent = $parent;
      if ($parent != null) {
          for (var k in $parent.prototype) {
              var f = $parent.prototype[k];
              if (f && f.$clone$) {
                  f = f.$clone$();
                  if (f == null) continue;
              }
              $template.prototype[k] = f;
          }
      }
  
      $template.$propertyInfo = {};
  
      $template.prototype.extend = function() {
          var c = this.getClazz(), l = arguments.length, f = arguments[l-1];
          if (pkg.instanceOf(this, pkg.$Extended) === false) {
              var cn = c.$name;
              c = Class(c, pkg.$Extended, []);
              c.$name = cn;
              this.getClazz = function() { return c; };
          }
  
          if (Array.isArray(f)) {
              for(var i=0; i < f.length; i++) {
                  var ff = f[i], n = FN(ff);
                  if (n === '') ff.call(this);
                  else {
                      var pv = this[n];
                      if (pv) {
                          if (this.hasOwnProperty(n) === false) {
                              if (pv.$clone$) pv = pv.$clone$();
                              else {
                                  var pvn = ProxyMethod(n);
                                  pvn.methods[pv.length] = pv;
                                  pv.boundTo = c;
                                  pv.methodName = n;
                                  pv = pvn;
                              }
                          }
                      }
                      else {
                          pv = ProxyMethod(n);
                      }
                      pv.methods[ff.length] = ff;
                      ff.boundTo = c;
                      ff.methodName = n;
                      // !!!
                      // Since method has been added dynamically class bean info has to be
                      // up to date. Do it below, but how !?
                      // ???
  
                      this[n] = pv;
                  }
              }
              l--;
          }
  
          // add new interfaces 
          for(var i=0; i < l; i++) {
              if (pkg.instanceOf(arguments[i], pkg.Interface) === false) {
                  throw new Error();
              }
              c.parents[arguments[i]] = true;
          }
          return this;
      };
  
      $template.prototype.$super = function() {
          if (pkg.$caller) {
              var name = pkg.$caller.methodName, $s = pkg.$caller.boundTo.$parent, args = arguments;
              if (arguments.length > 0 && typeof arguments[0] === 'function') {
                  name = arguments[0].methodName;
                  args = Array.prototype.slice.call(arguments, 1);
              }
  
              var params = args.length;
              while ($s != null) {
                  var m = $s.prototype[name];
                  if (m && (typeof m.methods === "undefined" || m.methods[params])) {
                      return m.apply(this, args);
                  }
                  $s = $s.$parent;
              }
              mnf.call(this, name, params);
          }
          throw new Error("$super is called outside of class context");
      };
  
      $template.prototype.getClazz = function() { return $template; };
      $template.prototype.$this    = function() { return pkg.$caller.boundTo.prototype[''].apply(this, arguments);  };
  
      $template.constructor.prototype.getMethods = function(name)  {
           var m = [];
           for (var n in this.prototype) {
               var f = this.prototype[n];
               if (arguments.length > 0 && name != n) continue;
               if (typeof f === 'function') {
                  if (f.$clone$) {
                      for (var mk in f.methods) m.push(f.methods[mk]);
                  }
                  else m.push(f);
               }
           }
           return m;
      };
  
      $template.constructor.prototype.getMethod = function(name, params) {
          var m = this.prototype[name];
          if (typeof m === 'function') {
              if (m.$clone$) {
                  if (typeof params === "undefined")  {
                      if (m.methods[0]) return m.methods[0];
                      for(var k in m.methods) return m.methods[k];
                      return null;
                  }
                  m = m.methods[params];
              }
              if (m) return m;
          }
          return null;
      };
  
      $template.extend = function(df) {
          if (Array.isArray(df) === false) throw new Error("Wrong class definition format " + df);
          for(var i=0; i < df.length; i++) {
              var f = df[i], n = FN(f);
              if (n[0] == "$") {
                  var ctx = n == "$prototype" ?  $template.prototype : (n == "$clazz" ? $template : null);
                  if (n) {
                      f.call(ctx);
                      continue;
                  }
              }
  
              if (f.boundTo) throw new Error("Method '" + n + "' is bound to other class");
              var sw = null, arity = f.length, vv = this.prototype[n];
  
              if (typeof vv === 'undefined') {
                  // this commented code allow to speed up proxy execution a  little bit for a single method
                  // sw = ProxyMethod(n, f);
                  // f.boundTo    = this;
                  // f.methodName = n;
                  // this.prototype[n] = sw;
                  // return;
                  sw = ProxyMethod(n);
              }
              else {
                  if (typeof vv === 'function') {
                      if (vv.$clone$) {
                          if (typeof vv.methods === "undefined") {
                              sw = ProxyMethod(n);
                              sw.methods[vv.f.length] = vv.f;
                          }
                          else sw = vv;
                      }
                      else {
                          sw = ProxyMethod(n);
                          if (vv.length != arity) {
                              vv.methodName = n;
                              vv.boundTo = this;
                          }
                          sw.methods[vv.length] = vv;
                      }
                  }
                  else throw new Error("Method '" + n + "' conflicts to property");
              }
  
              var pv = sw.methods[arity];
              if (typeof pv !== 'undefined' && pv.boundTo == this) {
                  throw new Error("Duplicated method '" + sw.methodName + "(" + arity +")'");
              }
  
              f.boundTo    = this;
              f.methodName = n;
              sw.methods[arity] = f;
              this.prototype[n] = sw;
          }
      };
  
      $template.extend(df);
  
      // validate constructor
      if ($template.$parent && $template.$parent.prototype[''] && typeof $template.prototype[''] === "undefined") {
          $template.prototype[''] = $template.$parent.prototype[''];
      }
  
      return $template;
  });
  
  var Class = pkg.Class, $cached = {}, $busy = 1, $f = [];
  
  function $cache(name, clazz) {
      if (($cached[name] && $cached[name] != clazz) || pkg.$global[name]) throw Error("Class name conflict: " + name);
      $cached[name] = clazz;
  }
  
  Class.forName = function(name) {
      if (pkg.$global[name]) return pkg.$global[name];
      //!!!!!! infinite cache !!!!
      if ($cached.hasOwnProperty(name) === false) $cache(name, eval(name));
      var cl = $cached[name];
      if (cl == null) throw new Error("Class " + name + " cannot be found");
      return cl;
  };
  
  pkg.instanceOf = function(obj, clazz) {
      if (clazz) {
          if (obj == null || typeof obj.getClazz === 'undefined')  return false;
          var c = obj.getClazz();
          return c != null && (c === clazz || (typeof c.parents !== 'undefined' && c.parents.hasOwnProperty(clazz)));
      }
      throw new Error("instanceOf(): unknown class");
  };
  
  pkg.ready = function() {
      if (arguments.length === 0) {
          if ($busy > 0) $busy--;
      }
      else {
          if (arguments.length == 1 && $busy === 0 && $f.length === 0) {
              arguments[0]();
              return;
          }
      }
  
      for(var i = 0; i < arguments.length; i++) $f.push(arguments[i]);
      while($busy === 0 && $f.length > 0) $f.shift()();
  };
  
  pkg.busy = function() { $busy++; };
  
  pkg.Output = Class([
      function $prototype() {
          this.print = function print(o) { this._p(0, o); };
          this.error = function error(o) { this._p(2, o); };
          this.warn  = function warn(o)  { this._p(1, o); };
  
          this._p = function(l, o) {
              o = this.format(o);
              if (pkg.isInBrowser) {
                  if (pkg.isIE) {
                      console.log(o);
                      // !!!! should check if IE9+ is used we can use  console.log
                      // alert(o);
                  }
                  else {
                      if (l === 0) console.log(o);
                      else {
                          if (l == 1) console.warn(o);
                          else console.error(o);
                      }
                  }
              }
              else pkg.$global.print(o);
          };
  
          this.format = function (o) {
              if (o && o.stack) return [o.toString(), ":",  o.stack.toString()].join("\n");
              if (o === null) return "<null>";
              if (typeof o === "undefined") return "<undefined>";
              if (isString(o) || isNumber(o) || isBoolean(o)) return o;
              var d = [o.toString() + " " + (o.getClazz?o.getClazz().$name:"") , "{"];
              for(var k in o) if (o.hasOwnProperty(k)) d.push("    " + k + " = " + o[k]);
              return d.join('\n') + "\n}";
          };
      }
  ]);
  
  pkg.Dummy = Class([]);
  
  pkg.HtmlOutput = Class(pkg.Output, [
      function() { this.$this(null); },
  
      function(element) {
          element = element || "zebra.out";
          if (pkg.isString(element)) {
              this.el = document.getElementById(element);
              if (this.el == null) {
                  this.el = document.createElement('div');
                  this.el.setAttribute("id", element);
                  document.body.appendChild(this.el);
              }
          }
          else {
              if (element == null) throw new Error("Unknown HTML output element");
              this.el = element;
          }
      },
  
      function print(s) { this.out('black', s); },
      function error(s) { this.out('red', s); },
      function warn(s)  { this.out('orange', s); },
  
      function out(color, msg) {
          var t = ["<div class='zebra.out.print' style='color:", color, "'>", this.format(msg), "</div>" ];
          this.el.innerHTML += t.join('');
      }
  ]);
  
  pkg.isInBrowser = typeof navigator !== "undefined";
  
  pkg.isIE      = pkg.isInBrowser && /msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent);
  pkg.isOpera   = pkg.isInBrowser && !/opera/i.test(navigator.userAgent);
  pkg.isChrome  = pkg.isInBrowser && typeof(window.chrome) !== "undefined";
  pkg.isSafari  = pkg.isInBrowser && !pkg.isChrome && /Safari/i.test(navigator.userAgent);
  pkg.isFF      = pkg.isInBrowser && window.mozInnerScreenX != null;
  pkg.isMobile  = pkg.isInBrowser && /Android|webOS|iPhone|iPad|iPod|BlackBerry|windows mobile|windows phone|IEMobile/i.test(navigator.userAgent);
  pkg.isTouchable = pkg.isInBrowser && (!!('ontouchstart' in window ) || !!('onmsgesturechange' in window));
  pkg.out       = new pkg.Output();
  pkg.isMacOS   = pkg.isInBrowser && navigator.platform.toUpperCase().indexOf('MAC') !== -1;
  
  pkg.print = function() { pkg.out.print.apply(pkg.out, arguments); };
  pkg.error = function() { pkg.out.error.apply(pkg.out, arguments); };
  pkg.warn  = function() { pkg.out.warn.apply(pkg.out, arguments); };
  
  function complete() {
      //!!! this code resolve names of classes  defined in a package
      //    should be re-worked to use more generic and trust-able mechanism
  
      pkg(function(n, p) {
          function collect(pp, p) {
              for(var k in p) {
                  if (k[0] != "$" && p.hasOwnProperty(k) && zebra.instanceOf(p[k], Class)) {
                      p[k].$name = pp ? [pp, k].join('.') : k;
                      collect(k, p[k]);
                  }
              }
          }
          collect(null, p);
      });
      pkg.ready();
  }
  
  if (pkg.isInBrowser) {
      var m = window.location.search.match(/[?&][a-zA-Z0-9_.]+=[^?&=]+/g), env = {};
      for(var i=0; m && i < m.length; i++) {
          var l = m[i].split('=');
          env[l[0].substring(1)] = l[1];
      }
      pkg(env);
  
      //               protocol[1]        host[2]  path[3]  querystr[4]
      var purl = /^([a-zA-Z_0-9]+\:)\/\/([^\/]*)(\/[^?]*)(\?[^?\/]*)?/;
      pkg.URL = function(url) {
          var a = document.createElement('a');
          a.href = url;
          var m = purl.exec(a.href);
  
          if (m == null) {
              m = purl.exec(window.location);
              if (m == null) throw Error("Cannot resolve '" + url + "' url");
              var p = m[3];
              a.href = m[1] + "//" + m[2] +  p.substring(0, p.lastIndexOf("/") + 1) + url;
              m = purl.exec(a.href);
          }
  
          this.path     = m[3];
          this.href     = a.href;
          this.protocol = m[1];
          this.host     = m[2];
          this.path     = this.path.replace(/[\/]+/g, "/");
          this.qs       = m[4];
      };
  
      pkg.URL.prototype.toString = function() { return this.href; };
  
      pkg.URL.prototype.getParentURL = function() {
          var i = this.path.lastIndexOf("/");
          if (i <= 0) throw new Error(this.toString() + " has no parent");
          var p = this.path.substring(0, i+1);
          return new pkg.URL([this.protocol, "//", this.host, p].join(''));
      };
  
      pkg.URL.isAbsolute = function(u) { return /^[a-zA-Z]+\:\/\//i.test(u);  };
  
      pkg.URL.prototype.join = function(p) {
          if (pkg.URL.isAbsolute(p)) throw new Error();
          return p[0] == '/' ? [ this.protocol, "//", this.host, p ].join('')
                             : [ this.protocol, "//", this.host, this.path, p ].join('');
      };
  
      if (window.addEventListener) {
          window.addEventListener('DOMContentLoaded', complete, false);
      }
      else {
          window.attachEvent('onload', complete);
      }
  }
  else {
      complete();
  }
  
  })();
  
  
  
  
  (function(pkg, Class) {
      var HEX = "0123456789ABCDEF";
      pkg.ID = function UUID(size) {
          if (typeof size === 'undefined') size = 16;
          var id = [];
          for (var i=0; i<36; i++)  id[i] = HEX[~~(Math.random() * 16)];
          return id.join('');
      };
  
      pkg.sleep = function() {
          var r = new XMLHttpRequest(), t = (new Date()).getTime().toString(), i = window.location.toString().lastIndexOf("?");
          r.open('GET', window.location + (i > 0 ? "&" : "?") + t, false);
          r.send(null);
      };
  
      // !!!
      // b64 is supposed to be used with binary stuff, applying it to utf-8 encoded data can bring to error
      // !!!
      var b64str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  
      pkg.b64encode = function(input) {
          var out = [], i = 0, len = input.length, c1, c2, c3;
          if (typeof ArrayBuffer !== "undefined") {
              if (input instanceof ArrayBuffer) input = new Uint8Array(input);
              input.charCodeAt = function(i) { return this[i]; };
          }
          if (Array.isArray(input)) input.charCodeAt = function(i) { return this[i]; };
  
          while(i < len) {
              c1 = input.charCodeAt(i++) & 0xff;
              out.push(b64str.charAt(c1 >> 2));
              if (i == len) {
                  out.push(b64str.charAt((c1 & 0x3) << 4), "==");
                  break;
              }
              c2 = input.charCodeAt(i++);
              out.push(b64str.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)));
              if (i == len) {
                  out.push(b64str.charAt((c2 & 0xF) << 2), "=");
                  break;
              }
              c3 = input.charCodeAt(i++);
              out.push(b64str.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6)), b64str.charAt(c3 & 0x3F));
          }
          return out.join('');
      };
  
      pkg.b64decode = function(input) {
          var output = [], chr1, chr2, chr3, enc1, enc2, enc3, enc4;
          input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
  
          while ((input.length % 4) !== 0) input += "=";
  
          for(var i=0; i < input.length;) {
              enc1 = b64str.indexOf(input.charAt(i++));
              enc2 = b64str.indexOf(input.charAt(i++));
              enc3 = b64str.indexOf(input.charAt(i++));
              enc4 = b64str.indexOf(input.charAt(i++));
  
              chr1 = (enc1 << 2) | (enc2 >> 4);
              chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
              chr3 = ((enc3 & 3) << 6) | enc4;
              output.push(String.fromCharCode(chr1));
              if (enc3 != 64) output.push(String.fromCharCode(chr2));
              if (enc4 != 64) output.push(String.fromCharCode(chr3));
          }
          return output.join('');
      };
  
      pkg.dateToISO8601 = function(d) {
          function pad(n) { return n < 10 ? '0'+n : n; }
          return [ d.getUTCFullYear(), '-', pad(d.getUTCMonth()+1), '-', pad(d.getUTCDate()), 'T', pad(d.getUTCHours()), ':',
                   pad(d.getUTCMinutes()), ':', pad(d.getUTCSeconds()), 'Z'].join('');
      };
  
      // http://webcloud.se/log/JavaScript-and-ISO-8601/
      pkg.ISO8601toDate = function(v) {
          var regexp = ["([0-9]{4})(-([0-9]{2})(-([0-9]{2})", "(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?",
                        "(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?"].join(''), d = v.match(new RegExp(regexp)),
                        offset = 0, date = new Date(d[1], 0, 1);
  
          if (d[3])  date.setMonth(d[3] - 1);
          if (d[5])  date.setDate(d[5]);
          if (d[7])  date.setHours(d[7]);
          if (d[8])  date.setMinutes(d[8]);
          if (d[10]) date.setSeconds(d[10]);
          if (d[12]) date.setMilliseconds(Number("0." + d[12]) * 1000);
          if (d[14]) {
              offset = (Number(d[16]) * 60) + Number(d[17]);
              offset *= ((d[15] == '-') ? 1 : -1);
          }
  
          offset -= date.getTimezoneOffset();
          date.setTime(Number(date) + (offset * 60 * 1000));
          return date;
      };
  
      pkg.parseXML = function(s) {
          function rmws(node) {
              if (node.childNodes !== null) {
                  for (var i = node.childNodes.length; i-->0;) {
                      var child= node.childNodes[i];
                      if (child.nodeType === 3 && child.data.match(/^\s*$/)) node.removeChild(child);
                      if (child.nodeType === 1) rmws(child);
                  }
              }
              return node;
          }
  
          if (typeof DOMParser !== "undefined") return rmws((new DOMParser()).parseFromString(s, "text/xml"));
          else {
              for (var n in { "Microsoft.XMLDOM":0, "MSXML2.DOMDocument":1, "MSXML.DOMDocument":2 }) {
                  var p = null;
                  try {
                      p = new ActiveXObject(n);
                      p.async = false;
                  }  catch (e) { continue; }
                  if (p === null) throw new Error("XML parser is not available");
                  p.loadXML(s);
                  return p;
              }
          }
          throw new Error("No XML parser is available");
      };
  
      pkg.QS = Class([
          function $clazz() {
              this.append = function (url, obj) {
                  return url + ((obj === null) ? '' : ((url.indexOf("?") > 0) ? '&' : '?') + pkg.QS.toQS(obj, true));
              };
  
              this.parse = function(url) {
                  var m = window.location.search.match(/[?&][a-zA-Z0-9_.]+=[^?&=]+/g), r = {};
                  for(var i=0; m && i < m.length; i++) {
                      var l = m[i].split('=');
                      r[l[0].substring(1)] = decodeURIComponent(l[1]);
                  }
                  return r;
              };
  
              this.toQS = function(obj, encode) {
                  if (typeof encode === "undefined") encode = true;
                  if (zebra.isString(obj) || zebra.isBoolean(obj) || zebra.isNumber(obj)) return "" + obj;
                  var p = [];
                  for(var k in obj) {
                      if (obj.hasOwnProperty(k)) p.push(k + '=' + (encode ? encodeURIComponent(obj[k].toString()) : obj[k].toString()));
                  }
                  return p.join("&");
              };
          }
      ]);
  
      //!!! type is intrenal IE specific parameter that indicates XDomainRequest or standard object has to be used
      pkg.getRequest = function(type) {
          if (zebra.isIE || type === 0 || type === 1) {
              if ((location.protocol.toLowerCase() === "file:" && type != 1) || type === 0) {
                  return new (function() {
                      var o = new ActiveXObject("MSXML2.XMLHTTP"), $this = this;
                      this.responseText = this.statusText = "";
                      this.onreadystatechange = this.responseXml = null;
                      this.readyState = this.status = 0;
                      this.__type = "aie";
                      o.onreadystatechange = function() {
                          $this.readyState = o.readyState;
                          if (o.readyState == 4) {
                              $this.responseText = o.responseText;
                              $this.responseXml  = o.responseXml;
                              $this.status     = o.status;
                              $this.statusText = o.statusText;
                          }
                          if ($this.onreadystatechange) $this.onreadystatechange();
                      };
  
                      this.open  = function(method, url, async, user, password) { return o.open(method, url, (async !== false), user, password); };
                      this.send  = function(data) { return o.send(data); };
                      this.abort = function(data) { return o.abort(); };
                      this.setRequestHeader = function(name, value) { o.setRequestHeader(name, value); };
                      this.getResponseHeader = function(name) { return o.getResponseHeader(name); };
                      this.getAllResponseHeaders = function() { return o.getAllResponseHeaders(); };
                  })();
              }
  
              var obj = new XDomainRequest();
              obj._open  = obj.open;
              obj._send  = obj.send;
              obj._async = true;
              obj.__type = "xie";
              obj.statusText = "";
              obj.status = obj.readyState = 0;
              obj.open = function(method, url, async, user, password) {
                  this._async = (async === true);
                  return this._open(method, url);
              };
              obj.setRequestHeader = obj.getResponseHeader = obj.getAllResponseHeaders = function () {
                  throw new Error("Method is not supported");
              };
  
              obj.send = function(data) {
                  var req = this;
                  this.onerror =function() { req.status = 404; };
                  this.onload = function() { req.status = 200; };
  
                  if (this._async === false) {
                      var result = this._send(data);
                      while (this.status === 0) {
                          pkg.sleep();
                      }
                      this.readyState = 4;
                      if (this.onreadystatechange) this.onreadystatechange();
                      return result;
                  }
                  return this._send(data);
              };
              return obj;
          }
  
          var r = new XMLHttpRequest();
          if (zebra.isFF) {
              r.__send = r.send;
              r.send = function(data) {
                  // !!! FF can throw NS_ERROR_FAILURE exception instead of returning 404 File Not Found HTTP error code
                  // !!! No request status, statusText are defined in this case
                  try { return this.__send(data); }
                  catch(e) {
                      if (!e.message || e.message.toUpperCase().indexOf("NS_ERROR_FAILURE") < 0) throw e;
                  }
              };
          }
          return r;
      };
  
      pkg.HTTP = Class([
          function(url) { this.url = url; },
  
          function GET()     { return this.GET(null, null); },
          function GET(f)    { return (typeof f === 'function') ? this.GET(null, f) : this.GET(f, null);  },
          function GET(d, f) { return this.SEND("GET", pkg.QS.append(this.url, d), null, f); },
  
          function POST()     { return this.POST(null, null); },
          function POST(d)    { return (typeof d === "function") ? this.POST(null, d) : this.POST(pkg.QS.toQS(d, false), null); },
          function POST(d, f) { return this.SEND("POST", this.url, d, f); },
  
          function SEND(method, url, data, callback) {
              //!!! IE9 returns 404 if XDomainRequest is used for the same domain but for different paths.
              //!!! Using standard XMLHttpRequest has to be forced in this case
              var t = zebra.isIE && (new zebra.URL(url)).host.toLowerCase() == location.host.toLowerCase() ? 0 : -1, 
                  r = pkg.getRequest(t), $this = this;
              
              if (callback !== null) {
                  r.onreadystatechange = function() {1
                      if (r.readyState == 4) {
                          $this.httpError(r, url);
                          callback(r.responseText, r);
                      }
                  };
              }
  
              r.open(method, url, callback !== null);
              r.send(data);
              if (callback === null) {
                  this.httpError(r, url);
                  return r.responseText;
              }
          },
  
          function httpError(r, url) { 
              if (r.status != 200) {
                  throw new Error("HTTP error " + r.status + " response = '" + r.responseText + "' url = " + url); 
              }
          }
      ]);
  
      pkg.GET = function(url) {
          var http = new pkg.HTTP(url);
          return http.GET.apply(http, Array.prototype.slice.call(arguments, 1));
      };
  
      pkg.POST = function(url) {
          var http = new pkg.HTTP(url);
          return http.POST.apply(http, Array.prototype.slice.call(arguments, 1));
      };
  
      var isBA = typeof(ArrayBuffer) !== 'undefined';
      pkg.InputStream = Class([
          function(container) {
              if (isBA && container instanceof ArrayBuffer) this.data = new Uint8Array(container);
              else {
                  if (zebra.isString(container)) {
                      this.Field(function read() { return this.available() > 0 ? this.data.charCodeAt(this.pos++) & 0xFF : -1; });
                  }
                  else {
                      if (Array.isArray(container) === false) throw new Error("Wrong type: " + typeof(container));
                  }
                  this.data = container;
              }
              this.marked = -1;
              this.pos    = 0;
          },
  
          function mark() {
              if (this.available() <= 0) throw new Error();
              this.marked = this.pos;
          },
  
          function reset() {
              if (this.available() <= 0 || this.marked < 0) throw new Error();
              this.pos    = this.marked;
              this.marked = -1;
          },
  
          function close()   { this.pos = this.data.length; },
          function read()    { return this.available() > 0 ? this.data[this.pos++] : -1; },
          function read(buf) { return this.read(buf, 0, buf.length); },
  
          function read(buf, off, len) {
              for(var i = 0; i < len; i++) {
                  var b = this.read();
                  if (b < 0) return i === 0 ? -1 : i;
                  buf[off + i] = b;
              }
              return len;
          },
  
          function readChar() {
              var c = this.read();
              if (c < 0) return -1;
              if (c < 128) return String.fromCharCode(c);
  
              var c2 = this.read();
              if (c2 < 0) throw new Error();
  
              if (c > 191 && c < 224) return String.fromCharCode(((c & 31) << 6) | (c2 & 63));
              else {
                  var c3 = this.read();
                  if (c3 < 0) throw new Error();
                  return String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
              }
          },
  
          function readLine() {
              if (this.available() > 0)
              {
                  var line = [], b;
                  while ((b = this.readChar()) != -1 && b != "\n") line.push(b);
                  var r = line.join('');
                  line.length = 0;
                  return r;
              }
              return null;
          },
  
          function available() { return this.data === null ? -1 : this.data.length - this.pos; },
          function toBase64() { return pkg.b64encode(this.data); }
      ]);
  
      pkg.URLInputStream = Class(pkg.InputStream, [
          function(url) {  this.$this(url, null); },
  
          function(url, f) {
              var r = pkg.getRequest(), $this = this;
              r.open("GET", url, f !== null);
              if (f === null || isBA === false) {
                  if (!r.overrideMimeType) throw new Error("Binary mode is not supported");
                  r.overrideMimeType("text/plain; charset=x-user-defined");
              }
  
              if (f !== null)  {
                  if (isBA) r.responseType = "arraybuffer";
                  r.onreadystatechange = function() {
                      if (r.readyState == 4) {
                          if (r.status != 200)  throw new Error(url);
                          $this.getClazz().$parent.getMethod('', 1).call($this, isBA ? r.response : r.responseText); // $this.$super(res);
                          f($this.data, r);
                      }
                  };
                  r.send(null);
              }
              else {
                  r.send(null);
                  if (r.status != 200) throw new Error(url);
                  this.$super(r.responseText);
              }
          },
  
          function close() {
              this.$super();
              if (this.data) {
                  this.data.length = 0;
                  this.data = null;
              }
          }
      ]);
  
      pkg.Service = Class([
          function(url, methods) {
              var $this = this;
              this.url = url;
              if (Array.isArray(methods) === false) methods = [ methods ];
  
              for(var i=0; i < methods.length; i++) {
                  (function() {
                      var name = methods[i];
                      $this[name] = function() {
                          var args = Array.prototype.slice.call(arguments);
                          if (args.length > 0 && typeof args[args.length - 1] == "function") {
                              var callback = args.pop();
                              return this.send(url, this.encode(name, args), function(res) { callback($this.decode(res)); } );
                          }
                          return this.decode(this.send(url, this.encode(name, args), null));
                      };
                  })();
              }
          },
  
          function send(url, data, callback) { return pkg.POST(url, data, callback); }
      ]);
  
      pkg.Service.invoke = function(clazz, url, method) {
          var rpc = new clazz(url, method);
          return function() { return rpc[method].apply(rpc, arguments); };
      };
  
      pkg.JRPC = Class(pkg.Service, [
          function(url, methods) {
              this.$super(url, methods);
              this.version = "2.0";
          },
  
          function encode(name, args) {
              return JSON.stringify({ jsonrpc: this.version, method: name, params: args, id: pkg.ID() });
          },
  
          function decode(r) {
              if (r === null || r.length === 0) throw new Error("Empty JSON result string");
              r = JSON.parse(r);
              if (typeof(r.error) !== "undefined") throw new Error(r.error.message);
              if (typeof r.result === "undefined" || typeof r.id === "undefined") throw new Error("Wrong JSON response format");
              return r.result;
          }
      ]);
  
      pkg.Base64 = function(s) { if (arguments.length > 0) this.encoded = pkg.b64encode(s); };
      pkg.Base64.prototype.toString = function() { return this.encoded; };
      pkg.Base64.prototype.decode   = function() { return pkg.b64decode(this.encoded); };
  
      pkg.XRPC = Class(pkg.Service, [
          function(url, methods) { this.$super(url, methods); },
  
          function encode(name, args) {
              var p = ["<?xml version=\"1.0\"?>\n<methodCall><methodName>", name, "</methodName><params>"];
              for(var i=0; i < args.length;i++) {
                  p.push("<param>");
                  this.encodeValue(args[i], p);
                  p.push("</param>");
              }
              p.push("</params></methodCall>");
              return p.join('');
          },
  
          function encodeValue(v, p)  {
              if (v === null) throw new Error("Null is not allowed");
  
              if (zebra.isString(v)) {
                  v = v.replace("<", "&lt;");
                  v = v.replace("&", "&amp;");
                  p.push("<string>", v, "</string>");
              }
              else {
                  if (zebra.isNumber(v)) {
                      if (Math.round(v) == v) p.push("<i4>", v.toString(), "</i4>");
                      else                    p.push("<double>", v.toString(), "</double>");
                  }
                  else {
                      if (zebra.isBoolean(v)) p.push("<boolean>", v?"1":"0", "</boolean>");
                      else {
                          if (v instanceof Date)  p.push("<dateTime.iso8601>", pkg.dateToISO8601(v), "</dateTime.iso8601>");
                          else {
                              if (Array.isArray(v))  {
                                  p.push("<array><data>");
                                  for(var i=0;i<v.length;i++) {
                                      p.push("<value>");
                                      this.encodeValue(v[i], p);
                                      p.push("</value>");
                                  }
                                  p.push("</data></array>");
                              }
                              else {
                                  if (v instanceof pkg.Base64) p.push("<base64>", v.toString(), "</base64>");
                                  else {
                                      p.push("<struct>");
                                      for(var k in v) {
                                          if (v.hasOwnProperty(k)) {
                                              p.push("<member><name>", k, "</name><value>");
                                              this.encodeValue(v[k], p);
                                              p.push("</value></member>");
                                          }
                                      }
                                      p.push("</struct>");
                                  }
                              }
                          }
                      }
                  }
              }
          },
  
          function decodeValue(node) {
              var tag = node.tagName.toLowerCase();
              if (tag == "struct")
              {
                   var p = {};
                   for(var i=0; i < node.childNodes.length; i++) {
                      var member = node.childNodes[i],  // <member>
                          key    = member.childNodes[0].childNodes[0].nodeValue.trim(); // <name>/text()
                      p[key] = this.decodeValue(member.childNodes[1].childNodes[0]);   // <value>/<xxx>
                  }
                  return p;
              }
              if (tag == "array") {
                  var a = [];
                  node = node.childNodes[0]; // <data>
                  for(var i=0; i < node.childNodes.length; i++) {
                      a[i] = this.decodeValue(node.childNodes[i].childNodes[0]); // <value>
                  }
                  return a;
              }
  
              var v = node.childNodes[0].nodeValue.trim();
              switch (tag) {
                  case "datetime.iso8601": return pkg.ISO8601toDate(v);
                  case "boolean": return v == "1";
                  case "int":
                  case "i4":     return parseInt(v, 10);
                  case "double": return Number(v);
                  case "base64":
                      var b64 = new pkg.Base64();
                      b64.encoded = v;
                      return b64;
                  case "string": return v;
              }
              throw new Error("Unknown tag " + tag);
          },
  
          function decode(r) {
              var p = pkg.parseXML(r), c = p.getElementsByTagName("fault");
              if (c.length > 0) {
                  var err = this.decodeValue(c[0].getElementsByTagName("struct")[0]);
                  throw new Error(err.faultString);
              }
              c = p.getElementsByTagName("methodResponse")[0];
              c = c.childNodes[0].childNodes[0]; // <params>/<param>
              if (c.tagName.toLowerCase() === "param") return this.decodeValue(c.childNodes[0].childNodes[0]); // <value>/<xxx>
              throw new Error("incorrect XML-RPC response");
          }
      ]);
  
      pkg.XRPC.invoke = function(url, method) { return pkg.Service.invoke(pkg.XRPC, url, method); };
      pkg.JRPC.invoke = function(url, method) { return pkg.Service.invoke(pkg.JRPC, url, method); };
  
  })(zebra("io"), zebra.Class);
  
  
  
  
  
  
  
  
  
  
  })();